【BZOJ2244】【SDOI2011】拦截导弹

21 篇文章 0 订阅

Description

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。
我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。

Input

第一行包含一个正整数 n ,表示敌军导弹数量;
下面 行按顺序给出了敌军所有导弹信息:
i+1 行包含2个正整数 hi vi ,分别表示第 枚导弹的高度和速度。

Output

输出包含两行。
第一行为一个正整数,表示最多能拦截掉的导弹数量;
第二行包含 n 0 1 之间的实数,第i个数字表示第i枚导弹被拦截掉的概率(你可以保留任意多位有效数字)。

Sample Input

4
3 30
4 40
6 60
3 30

Sample Output

2
0.33333 0.33333 0.33333 1.00000

数据规模和约定

对于 100% 的数据, 1n5×104 1hivi109
均匀分布着约 30% 的数据,所有 vi 均相等。
均匀分布着约 50% 的数据,满足 1hivi1000

题解

一道综合数据结构与 DP 的好题。

首先,题目要求求一个最长二维不上升序列的长度,这可以通过 CDQ 分治 + 树状数组解决。转移方式同 O(nlog2n) 的最长不上升子序列。

然后,题目要求求出随机选取一个最初不上升子序列,每个点被选中的概率。这可以通过类似 LIS 计数的方法解决。也是和一维的情况类似,可以在dp的过程中记录以当前位置结束的最长不上升子序列的数量 g(0,i) 和以当前位置开头的最长不上升子序列的数量 g(1,i) g(0,i)×g(1,i) 就是选中当前节点的最长不上升子序列的数量,从而求得概率。

总复杂度 O(nlog22n)

My Code

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdio>

using namespace std;
typedef long long ll;

struct node {
    int x, y, id, ans;
} d[100005], t[100005];

int n;
int h[100005], c[100005];
int th[100005], tc[100005];
int bit[1000005];
double cnt[1000005];

inline int lowbit(int x) {
    return x & -x;
}

inline void add(int x, int w, double v) {
    for(int i = x; i <= n; i += lowbit(i)) {
        if(bit[i] == w) {
            cnt[i] += v;
        } else if(bit[i] < w) {
            bit[i] = w;
            cnt[i] = v;
        }
    }
}

inline int qmax(int x) {
    int sum = 0;
    for(int i = x; i; i -= lowbit(i)) {
        if(bit[i] > sum) sum = bit[i];
    }
    return sum;
}

inline double qsum(int x, int mx) {
    double sum = 0;
    for(int i = x; i; i -= lowbit(i)) {
        if(bit[i] == mx) sum += cnt[i];
    }
    return sum;
}

inline void del(int x) {
    for(int i = x; i <= n; i += lowbit(i)) {
        cnt[i] = 0;
        bit[i] = 0;
    }
}

int f[2][100005];
double g[2][100005];

int cmp1(node a, node b) {
    return a.x < b.x;
}

int cmp2(node a, node b) {
    return a.id < b.id;
}


void cdq1(int l, int r) {
    if(l == r) return;
    int mid = (l + r) >> 1;
    cdq1(l, mid);
    int p = l, q = mid + 1;
    sort(d + l, d + mid + 1, cmp1);
    sort(d + mid + 1, d + r + 1, cmp1);
    for(int i = l; i <= r; i ++) {
        if((p <= mid && d[p].x <= d[q].x) || q > r) {
            add(d[p].y, f[0][d[p].id], g[0][d[p].id]);
            p++;
        } else {
            int tmax = qmax(d[q].y);

            if(f[0][d[q].id] < tmax + 1) {
                f[0][d[q].id] = tmax + 1;
                g[0][d[q].id] = qsum(d[q].y, tmax);
            } else if(f[0][d[q].id] == tmax + 1) {
                g[0][d[q].id] += qsum(d[q].y, tmax);
            }

            q++;
        }
    }
    for(int i = l; i <= mid; i ++) {
        del(d[i].y);
    }
    sort(d + l, d + r + 1, cmp2);

    cdq1(mid + 1, r);
    sort(d + l, d + r + 1, cmp2);
}

void cdq2(int l, int r) {
    if(l == r) return;
    int mid = (l + r) >> 1;
    cdq2(l, mid);
    int p = l, q = mid + 1;
    sort(d + l, d + mid + 1, cmp1);
    sort(d + mid + 1, d + r + 1, cmp1);
    for(int i = l; i <= r; i ++) {
        if((p <= mid && d[p].x <= d[q].x) || q > r) {
            add(d[p].y, f[1][d[p].id], g[1][d[p].id]);
            p++;
        } else {
            int tmax = qmax(d[q].y);
            if(f[1][d[q].id] < tmax + 1) {
                f[1][d[q].id] = tmax + 1;
                g[1][d[q].id] = qsum(d[q].y, tmax);
            } else if(f[1][d[q].id] == tmax + 1) {
                g[1][d[q].id] += qsum(d[q].y, tmax);
            }
            q++;
        }
    }
    for(int i = l; i <= mid; i ++) {
        del(d[i].y);
    }
    sort(d + l, d + r + 1, cmp2);
    cdq2(mid + 1, r);
    sort(d + l, d + r + 1, cmp2);
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) {
        scanf("%d%d", &h[i], &c[i]);
        th[i] = h[i];
        tc[i] = c[i];
    }
    sort(th + 1, th + n + 1);
    sort(tc + 1, tc + n + 1);
    for(int i = 1; i <= n; i ++) {
        h[i] = lower_bound(th + 1, th + n + 1, h[i]) - th;
        c[i] = lower_bound(tc + 1, tc + n + 1, c[i]) - tc;
        d[i].x = n - h[i] + 1, d[i].y = n - c[i] + 1;
        d[i].id = i;
    }
    for(int i = 1; i <= n; i ++) f[0][i] = f[1][i] = g[0][i] = g[1][i] = 1;
    cdq1(1, n);
    for(int i = 1; i <= n; i ++) {
        d[i].x = h[n - i + 1], d[i].y = c[n - i + 1];
        d[i].id = i;
    }
    cdq2(1, n);
    int ans = 0;
    for(int i = 1; i <= n; i ++) {
        ans = max(ans, f[0][i]);
    }
    printf("%d\n", ans);
    double sum = 0;
    for(int i = 1; i <= n; i ++) {
        if(f[0][i] == ans) sum += g[0][i];
    }
    for(int i = 1; i <= n; i ++) {
        if(f[0][i] + f[1][n - i + 1] - 1 != ans) printf("%.5lf ", 0.0);
        else printf("%.5lf ", double(g[0][i]) * double(g[1][n - i + 1]) / sum);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值