省流:简单题,但是代码出锅写了半天。
先考虑第一问,若 $i \lt j, a_i \leq a_j, b_i \leq b_j$ 则建边 $i \to j$,求最长路径。
这是好做的,显然三维偏序。
然后第二问:对于每一个节点 $i$,求 $i$ 在路径上的概率。
保证总方案数不超过 C++ 中 double 类型的存储范围。
所以直接用 double 存总方案数即可,再设 $f_i, g_i$ 表示 $1 \to i$ 和 $i \to end$ 的方案数,显然这两段路径都要长度极大。
然后一样地三维偏序过程中记录方案数之和即可。
具体实现:分别正反做 cdq 分治,按照正常三维偏序的板子套计数即可。
一开始方案数忘记加上第三维的限制了(只拿了一个桶记)所以要把它和最大值一起扔一个 pair 维护。
#include <bits/stdc++.h>
#define PID pair<int, double>
using namespace std;
const int N = 5e4 + 5;
int n, X[N], Y[N];
struct node { int id, a, b, dis; double c; } a[N];
inline bool cmpid(node a, node b) { return a.id < b.id; }
inline bool cmpa1(node a, node b) { return a.a > b.a; }
inline bool cmpa2(node a, node b) { return a.a < b.a; }
PID tr[N]; // 树状数组 -- Max of dist、方案数 cnt
PID merge(PID a, int d, double c) {
if (d < a.first) return a;
if (d == a.first) return {a.first, a.second + c};
return {d, c};
}
inline void add(int x, int d, double c) { for ( ; x ; x -= x & -x) tr[x] = merge(tr[x], d, c); }
inline PID query(int x) { PID res = {0, 0}; for ( ; x < N ; x += x & -x) res = merge(res, tr[x].first, tr[x].second); return res; }
inline void clr(int x, int d) { for ( ; x ; x -= x & -x) tr[x] = {0, 0}; }
void cdq1(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq1(l, mid);
sort(a + l, a + mid + 1, cmpa1);
sort(a + mid + 1, a + r + 1, cmpa1);
for (int i = l, j = mid + 1, k = l; k <= r; k++) {
if ((j > r) || (i <= mid && a[i].a >= a[j].a)) add(a[i].b, a[i].dis, a[i].c), i++;
else {
auto [Max, cnt] = query(a[j].b); // 左边能提供的最长长度
if (Max + 1 > a[j].dis) a[j].dis = Max + 1, a[j].c = cnt;
else if (Max + 1 == a[j].dis) a[j].c += cnt;
j++;
}
}
for (int i = l; i <= mid; i++) clr(a[i].b, a[i].dis);
sort(a + mid + 1, a + r + 1, cmpid);
cdq1(mid + 1, r);
}
inline void add2(int x, int d, double c) { for ( ; x < N; x += x & -x) tr[x] = merge(tr[x], d, c); }
inline PID query2(int x) { PID res = {0, 0}; for ( ; x ; x -= x & -x) res = merge(res, tr[x].first, tr[x].second); return res; }
inline void clr2(int x, int d) { for ( ; x < N ; x += x & -x) tr[x] = {0, 0}; }
void cdq2(int l, int r) {
if (l == r) return;
int mid = l + r >> 1;
cdq2(mid + 1, r);
sort(a + l, a + mid + 1, cmpa2);
sort(a + mid + 1, a + r + 1, cmpa2);
for (int i = l, j = mid + 1, k = l; k <= r; k++) {
if ((i > mid) || (j <= r && a[i].a >= a[j].a)) add2(a[j].b, a[j].dis, a[j].c), j++;
else {
auto [Max, cnt] = query2(a[i].b); // 左边能提供的最长长度
if (Max + 1 > a[i].dis) a[i].dis = Max + 1, a[i].c = cnt;
else if (Max + 1 == a[i].dis) a[i].c += cnt;
i++;
}
}
for (int i = mid + 1; i <= r; i++) clr2(a[i].b, a[i].dis);
sort(a + l, a + mid + 1, cmpid);
cdq2(l, mid);
}
double all, ans[N];
int dist[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].a, &a[i].b), a[i].dis = 1, a[i].c = 1, a[i].id = i, X[i] = a[i].a, Y[i] = a[i].b;
sort(X + 1, X + 1 + n), sort(Y + 1, Y + 1 + n);
int m1 = unique(X + 1, X + 1 + n) - X - 1, m2 = unique(Y + 1, Y + 1 + n) - Y - 1;
for (int i = 1; i <= n; i++) a[i].a = lower_bound(X + 1, X + 1 + m1, a[i].a) - X, a[i].b = lower_bound(Y + 1, Y + 1 + m2, a[i].b) - Y;
cdq1(1, n);
sort(a + 1, a + 1 + n, cmpid);
// for (int i = 1; i <= n; i++) cout << a[i].dis << ' '; puts("");
int Max = 0; for (int i = 1; i <= n; i++) Max = max(Max, a[i].dis), dist[i] = a[i].dis;
printf("%d\n", Max);
for (int i = 1; i <= n; i++) {
ans[i] = a[i].c;
if (a[i].dis == Max) all += a[i].c;
a[i].c = 1, a[i].dis = 1;
}
cdq2(1, n);
sort(a + 1, a + 1 + n, cmpid);
// for (int i = 1; i <= n; i++) cout << a[i].dis << ' '; puts("");
for (int i = 1; i <= n; i++)
if ((dist[i] + a[i].dis - 1) ^ Max) printf("0.00000 ");
else printf("%.5lf ", ans[i] * a[i].c / all);
return 0;
}