某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。
分析
这道题,恶心至极。
调了一天......
从我第一次提交到 AC 的时间可以看出我调了多久,还不包括连样例都没过的时候。
第一问就是一个 dp 最大值,但它是三维的,我们可以用 CDQ 来解决。
麻烦在于第二问。
显然,一个点被选到的概率为经过改点的路径条数 / 在 LIS 上的总路径条数,而判断这个点是否在 LIS 上,我们可以通过对正反两面分别 CDQ 一次,求出 dp 值 f[i] ,并记录路径条数 g[i] 。
如果当前节点满足 \(f[i][0]+f[i][1]-1=\max f[j][0](j\in [1,n])\) ,这里的 -1 是去掉对改节点的重复计算,那么它被选中的概率为 \(\frac{g[i][0]\times g[i][1]}{\sum_{j\in LIS}{g[j][0]}}\) 。
复杂度:\(O(n\log n)\)
代码
CDQ 内 sort 函数一定要这样写(想一想,为什么?)
为什么我也不知道,通过不断的调试发现只有这样写才能对。
//========================= // author:hliwen // date:2020.2.1 //========================= #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 50003 #define DEBUG puts("ok") #define tie0 cin.tie(0),cout.tie(0) #define fastio ios::sync_with_stdio(false) #define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout) using namespace std; typedef double db; typedef long long ll; template <typename T> inline void read(T &x) { T f = 1; x = 0; char c; for (c = getchar(); !isdigit(c); c = getchar()) if (c == '-') f = -1; for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48); x *= f; } struct ele { int h, v, id, f[2]; db g[2]; } e[N], r[N]; db sum; int n, ans; int hh[N], hv[N]; struct TreeArray { int f[N]; db g[N]; int lowbit(int i) { return i & -i; } void update(int i, int k, db c) { while (i <= n) { if (f[i] < k) f[i] = k, g[i] = c; else if (f[i] == k) g[i] += c; i += lowbit(i); } } int query(int i, db &c) { int ret = 0; c = 0; while (i) { if (f[i] > ret) ret = f[i], c = g[i]; else if (f[i] == ret) c += g[i]; i -= lowbit(i); } return ret; } void del(int i) { while (i <= n) { f[i] = 0, g[i] = 0; i += lowbit(i); } } } tr; bool cmpid(ele x, ele y) { return x.id < y.id; } bool cmph(ele x, ele y) { return x.h < y.h; } void CDQ(int l, int r, int opt) { if (l == r) return; int mid = l + r >> 1; sort(&e[l], &e[r+1], cmpid); CDQ(l, mid, opt); sort(&e[l], &e[mid+1], cmph), sort(&e[mid+1], &e[r+1], cmph); int i, j; for (i = l, j = mid + 1; j <= r; ++j) { while (i <= mid && e[i].h <= e[j].h) tr.update(e[i].v, e[i].f[opt], e[i].g[opt]), i++; int x; db y; x = tr.query(e[j].v, y) + 1; if (x > e[j].f[opt]) e[j].f[opt] = x, e[j].g[opt] = y; else if (x == e[j].f[opt]) e[j].g[opt] += y; } for (i = l; i <= mid; ++i) tr.del(e[i].v); CDQ(mid + 1, r, opt); } int main() { read(n); for (int i = 1; i <= n; ++i) { read(e[i].h), read(e[i].v), e[i].id = i; hh[i] = e[i].h, hv[i] = e[i].v; e[i].f[0] = e[i].f[1] = 1; e[i].g[0] = e[i].g[1] = 1.0; } int x, y; sort(hh + 1, hh + 1 + n), sort(hv + 1, hv + 1 + n); x = unique(hh + 1, hh + 1 + n) - hh - 1, y = unique(hv + 1, hv + 1 + n) - hv - 1; for (int i = 1; i <= n; ++i) { e[i].h = x - (lower_bound(hh + 1, hh + 1 + x, e[i].h) - hh) + 1; e[i].v = y - (lower_bound(hv + 1, hv + 1 + y, e[i].v) - hv) + 1; } CDQ(1, n, 0); sort(e + 1, e + 1 + n, cmpid); reverse(e + 1, e + 1 + n); for (int i = 1; i <= n; ++i) { e[i].id = i; e[i].h = x + 1 - e[i].h, e[i].v = y + 1 - e[i].v; if (ans < e[i].f[0]) ans = e[i].f[0], sum = e[i].g[0]; else if (ans == e[i].f[0]) sum += e[i].g[0]; } CDQ(1, n, 1); sort(e + 1, e + 1 + n, cmpid); printf("%d\n", ans); for (int i = n; i; --i) { if (e[i].f[0] + e[i].f[1] - 1 == ans) printf("%.5f ", e[i].g[0] * e[i].g[1] / sum); else printf("0.00000 "); } return 0; }
以下是带调试代码的版本
可以看出我的调试方法有多低级了.......(我连 gdb 都不会)
来源:https://www.cnblogs.com/hlw1/p/12247450.html