洛谷P2487 [SDOI2011] 拦截导弹 题解 CDQ分治优化DP
题目链接:https://www.luogu.com.cn/problem/P2487
解题思路完全来自:
其中最主要的是 hsfzLZH1 大佬 题解中的这段表述:
这个数据结构(线段树/树状数组)需要进行两种操作:
- 修改单点的值;
- 查询区间的最大值和对应的数值。
实现时,我是用了线段树。
- \(j \lt i\)
- \(h_j \ge h_i\)
- \(v_j \ge v_i\)
\(f1_i\) 是所有满足上述条件的 \(j\) 对应的 \(\max\limits_{j} f1_j + 1\)
\(g1_i\) 是所有满足上述条件且 \(f1_j + 1 = f_i\) 的 \(g1_j\) 之和。
\(f2_i\) 和 \(g2_i\) 也是类似的操作。
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
struct Segtree {
int maxf[maxn<<2];
double sumg[maxn<<2];
#define lson l, mid, u<<1
#define rson mid+1, r, u<<1|1
void push_up(int u) {
int f1 = maxf[u<<1], f2 = maxf[u<<1|1];
double g1 = sumg[u<<1], g2 = sumg[u<<1|1];
if (f1 > f2)
maxf[u] = f1, sumg[u] = g1;
else if (f2 > f1)
maxf[u] = f2, sumg[u] = g2;
else
maxf[u] = f1, sumg[u] = g1 + g2;
}
void clean(int l, int r, int u) {
if (!maxf[u])
return;
maxf[u] = sumg[u] = 0;
if (l == r)
return;
int mid = l + r >> 1;
clean(lson);
clean(rson);
}
void add(int v, int f, double g, int l, int r, int u) {
if (l == r) {
if (f > maxf[u]) {
maxf[u] = f;
sumg[u] = g;
}
else if (f == maxf[u]) {
sumg[u] += g;
}
return;
}
int mid = l + r >> 1;
if (v <= mid)
add(v, f, g, lson);
else
add(v, f, g, rson);
push_up(u);
}
pair<int, double> query(int L, int R, int l, int r, int u) {
if (L <= l && r <= R)
return { maxf[u], sumg[u] };
int mf = 0;
double sg = 0;
int mid = l + r >> 1;
if (L <= mid) {
auto [f, g] = query(L, R, lson);
if (f > mf)
mf = f, sg = g;
else if (f == mf)
sg += g;
}
if (R > mid) {
auto [f, g] = query(L, R, rson);
if (f > mf)
mf = f, sg = g;
else if (f == mf)
sg += g;
}
return { mf, sg };
}
} segt;
struct Node {
int id, h, v, f;
double g;
} a[maxn], b[maxn];
int n, m;
void init() {
vector<int> vec;
for (int i = 1; i <= n; i++) vec.push_back(a[i].v);
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end());
m = vec.size();
for (int i = 1; i <= n; i++)
a[i].v = lower_bound(vec.begin(), vec.end(), a[i].v) - vec.begin() + 1;
copy(a+1, a+n+1, b+1);
}
void cdq1(int l, int r) {
if (l >= r)
return;
int mid = l + r >> 1;
cdq1(l, mid);
segt.clean(1, m, 1);
sort(a+l, a+mid+1, [](auto a, auto b) {
return a.h > b.h;
});
sort(a+mid+1, a+r+1, [](auto a, auto b) {
return a.h > b.h;
});
for (int i = mid+1, j = l; i <= r; i++) {
for (; j <= mid && a[j].h >= a[i].h; j++) {
segt.add(a[j].v, a[j].f, a[j].g, 1, m, 1);
}
auto [f, g] = segt.query(a[i].v, m, 1, m, 1);
if (f + 1 > a[i].f)
a[i].f = f + 1, a[i].g = g;
else if (f + 1 == a[i].f)
a[i].g += g;
}
sort(a+mid+1, a+r+1, [](auto a, auto b) {
return a.id < b.id;
});
cdq1(mid+1, r);
}
void cdq2(int l, int r) {
if (l >= r)
return;
int mid = l + r >> 1;
cdq2(mid+1, r);
segt.clean(1, m, 1);
sort(b+l, b+mid+1, [](auto a, auto b) {
return a.h < b.h;
});
sort(b+mid+1, b+r+1, [](auto a, auto b) {
return a.h < b.h;
});
for (int i = l, j = mid+1; i <= mid; i++) {
for (; j <= r && b[i].h >= b[j].h; j++) {
segt.add(b[j].v, b[j].f, b[j].g, 1, m, 1);
}
auto [f, g] = segt.query(1, b[i].v, 1, m, 1);
if (f + 1 > b[i].f)
b[i].f = f + 1, b[i].g = g;
else if (f + 1 == b[i].f)
b[i].g += g;
}
sort(b+l, b+mid+1, [](auto a, auto b) {
return a.id < b.id;
});
cdq2(l, mid);
}
int main() {
scanf("%d", &n);
for (int i = 1, h, v; i <= n; i++) {
scanf("%d%d", &h, &v);
a[i] = { i, h, v, 1, 1 };
}
init();
cdq1(1, n);
cdq2(1, n);
sort(a+1, a+n+1, [](auto a, auto b) {
return a.id < b.id;
});
sort(b+1, b+n+1, [](auto a, auto b) {
return a.id < b.id;
});
int ans = 0;
double tot = 0;
for (int i = 1; i <= n; i++) {
if (a[i].f > ans)
ans = a[i].f, tot = a[i].g;
else if (a[i].f == ans)
tot += a[i].g;
}
printf("%d\n", ans);
for (int i = 1; i <= n; i++) {
double tmp;
if (a[i].f + b[i].f - 1 == ans)
tmp = a[i].g * b[i].g / tot;
else
tmp = 0;
printf("%lf ", tmp);
}
return 0;
}
浙公网安备 33010602011771号