洛谷 P2487 [SDOI2011]拦截导弹

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度、并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小、也就是拦截导弹的数量最多的方案。但是拦截导弹数量的最多的方案有可能有多个,如果有多个最优方案,那么我们会随机选取一个作为最终的拦截导弹行动蓝图。

我方间谍已经获取了所有敌军导弹的高度和速度,你的任务是计算出在执行上述决策时,每枚导弹被拦截掉的概率。

输入输出格式

输入格式:

输出格式:

输入输出样例

输入样例#1:

4
3 30
4 40
6 60
3 30

输出样例#1:

2
0.33333 0.33333 0.33333 1.00000

说明

题目大意:求二维LDS,以及每个位置在LDS中出现的概率

破题

第14个点大概是有问题

头一次见到用double的计数题

h和v太大了,显然要先离散化

先考虑第一个问题,这是一个三维偏序关系,显然可以cdq分治解决,注意分治的顺序是左中右不是左右中

然后是第二个问题,考虑在cdq分治的时候同时记录一下LDS的个数,那么一个位置出现的概率就是(经过这个位置的LDS数量/总的LDS数量)

怎么求经过一个位置的LDS数量呢,我们考虑倒过来再做一遍二维LIS,显然如果一个位置在某个LDS中,那么 以它结尾的LDS长度+倒过来以它结尾的LIS长度=整个序列的LDS长度+1,而经过这个位置的LDS数量则是 以他结尾的LDS数量*倒过来以他结尾的LIS数量

然而这个东西longlong都存不下,必须开double

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <map>
#include <stack>
#include <set>
#include <vector>
#include <queue>
#include <time.h>
#define eps 1e-7
#define INF 0x3f3f3f3f
#define MOD 1000000007
#define rep0(j,n) for(int j=0;j<n;++j)
#define rep1(j,n) for(int j=1;j<=n;++j)
#define pb push_back
#define mp make_pair
#define set0(n) memset(n,0,sizeof(n))
#define ll long long
#define ull unsigned long long
#define iter(i,v) for(edge *i=head[v];i;i=i->nxt)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define print_runtime printf("Running time:%.3lfs\n",double(clock())/1000.0)
#define TO(j) printf(#j": %d\n",j);
//#define OJ
using namespace std;
const int MAXINT = 50010;
const int MAXNODE = 100010;
const int MAXEDGE = 2 * MAXNODE;
char BUF, *buf;
int read() {
    char c = getchar(); int f = 1, x = 0;
    while (!isdigit(c)) { if (c == ‘-‘) f = -1; c = getchar(); }
    while (isdigit(c)) { x = x * 10 + c - ‘0‘; c = getchar(); }
    return f * x;
}
char get_ch() {
    char c = getchar();
    while (!isalpha(c)) c = getchar();
    return c;
}
//------------------- Head Files ----------------------//

int n, b[MAXINT], cnt_b, c[MAXINT], cnt_c;
double prop[MAXINT];
struct res {
    int v;
    double cnt;
    res(int _v, double _c) : v(_v), cnt(_c) {}
    res() {}
};
res add(res a, res b) {
    if (a.v == b.v) return res(a.v, a.cnt + b.cnt);
    if (a.v > b.v) return a;
    else return b;
}
struct missile {
    int h, v, id;
    res ans;
} a[MAXINT], s[MAXINT];
struct node {
    int lb, rb;
    res ans;
    int tag;
    node *l, *r;
    void pushup() {
        ans = add(l->ans, r->ans);
    }
    void clear() {
        ans = res(-1, -1);
        tag = 0;
    }
    void pushdown() {
        if (tag != -1) {
            l->clear();
            r->clear();
            tag = -1;
        }
    }
};

struct SegTree {
    node mp[MAXINT * 4];
    node *root;
    int cnt;
    node *newnode(int l, int r) {
        node *p = &mp[cnt++];
        p->lb = l; p->rb = r;
        return p;
    }
    void buildtree(int l, int r, node *&p) {
        p = newnode(l, r);
        if (r - l > 1) {
            buildtree(l, (l + r) / 2, p->l);
            buildtree((l + r) / 2, r, p->r);
        }
    }
    void clear() {
        root->clear();
    }
    void insert(int pos, res v) {
        insert(pos, v, root);
    }
    void insert(int pos, res v, node *p) {
        if (p->rb - p->lb == 1) {
            p->ans = add(p->ans, v);
            return;
        }
        p->pushdown();
        int mid = (p->lb + p->rb) / 2;
        if (pos < mid) insert(pos, v, p->l);
        else        insert(pos, v, p->r);
        p->pushup();
    }
    res query(int l) {
        res ret = query(l, INF, root);
        ret.v++;
        return ret;
    }
    res query2(int r) {
        res ret = query(0, r + 1, root);
        ret.v++;
        return ret;
    }
    res query(int l, int r, node *p) {
        if (r <= p->lb || l >= p->rb) return res(-1, -1);
        if (r >= p->rb && l <= p->lb) return p->ans;
        p->pushdown();
        return add(query(l, r, p->l), query(l, r, p->r));
    }
} st;
bool cmph(const missile & a, const missile & b) {
    return a.h > b.h;
}
bool cmph2(const missile & a, const missile & b) {
    return a.h < b.h;
}
bool cmpid(const missile & a, const missile & b) {
    return a.id < b.id;
}
bool cmpid2(const missile & a, const missile & b) {
    return a.id > b.id;
}
void cdq(int l, int r) {
    int mid = (l + r) / 2;
    if (r - l == 1) return;
    sort(a + l, a + r, cmpid);
    cdq(l, mid);
    sort(a + l, a + mid, cmph);
    sort(a + mid, a + r, cmph);

    int mx = 0, cnt = 0, i = l, j = mid;
    st.clear();
    for (; i < mid; i++) {
        for (; j < r && a[j].h > a[i].h; j++) {
            a[j].ans = add(a[j].ans, st.query(a[j].v));
        }
        st.insert(a[i].v, a[i].ans);
    }
    for (; j < r; j++) {
        a[j].ans = add(a[j].ans, st.query(a[j].v));
    }
    cdq(mid, r);
}
void cdq2(int l, int r) {
    int mid = (l + r) / 2;
    if (r - l == 1) return;
    sort(s + l, s + r, cmpid2);
    cdq2(l, mid);
    sort(s + l, s + mid, cmph2);
    sort(s + mid, s + r, cmph2);

    int mx = 0, cnt = 0, i = l, j = mid;
    st.clear();
    for (; i < mid; i++) {
        for (; j < r && s[j].h < s[i].h; j++) {
            s[j].ans = add(s[j].ans, st.query2(s[j].v));
        }
        st.insert(s[i].v, s[i].ans);
    }
    for (; j < r; j++) {
        s[j].ans = add(s[j].ans, st.query2(s[j].v));
    }
    cdq2(mid, r);
}
void get_input();
void work();
int main() {
    get_input();
    work();
    return 0;
}

void work() {
    st.buildtree(0, n, st.root);
    rep0(i, n) s[i] = a[i];
    sort(s, s + n, cmpid2);
    cdq(0, n);
    cdq2(0, n);
    res ans(0, 0);
    rep0(i, n) {
        ans = add(ans, a[i].ans);
    }

    sort(a, a + n, cmpid);
    sort(s, s + n, cmpid);
    rep0(i, n) {
        if (s[i].ans.v + a[i].ans.v != ans.v + 1) prop[i] = 0; else {
            prop[i] = s[i].ans.cnt*a[i].ans.cnt / double(ans.cnt);
        }
    }
    printf("%lld\n", ans.v);
    rep0(i, n) printf("%.5lf ", prop[i]);
    putchar(‘\n‘);
}
void get_input() {
    n = read();
    rep0(i, n) {
        a[i].id = i;
        a[i].h = read(); a[i].v = read(); a[i].ans = res(1, 1);
        b[cnt_b++] = a[i].h;
        c[cnt_c++] = a[i].v;
    }
    sort(b, b + cnt_b);
    sort(c, c + cnt_c);
    cnt_b = unique(b, b + cnt_b) - b;
    cnt_c = unique(c, c + cnt_c) - c;
    rep0(i, n) {
        a[i].h = lower_bound(b, b + cnt_b, a[i].h) - b;
        a[i].v = lower_bound(c, c + cnt_c, a[i].v) - c;
    }
}
时间: 2024-10-01 06:13:25

洛谷 P2487 [SDOI2011]拦截导弹的相关文章

bzoj 2244: [SDOI2011]拦截导弹 cdq分治

2244: [SDOI2011]拦截导弹 Time Limit: 30 Sec  Memory Limit: 512 MBSec  Special JudgeSubmit: 237  Solved: 103[Submit][Status][Discuss] Description 某 国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截任意速度的导 弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度

BZOJ 2244: [SDOI2011]拦截导弹 DP+CDQ分治

2244: [SDOI2011]拦截导弹 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度.并且能够拦截任意速度的导弹,但是以后每一发炮弹都不能高于前一发的高度,其拦截的导弹的飞行速度也不能大于前一发.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹. 在不能拦截所有的导弹的情况下,我们当然要选择使国家损失最小.也就是拦截导弹的数量最多的方案.但是

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj2244[SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 第$i$个导弹看成一个三元组$(i,h_i,v_i)$ 其实就是最长上升子序列的问题. 我们分别求以第$i$个导弹为结尾的最长上升子序列的长度和个数,以及以第$i$个导弹为开头的最长上升子序列的长度和个数. 下面以求以第$i$个导弹为结尾的最长上升子序列的长度和个数为例. 记以第$i$个导弹结尾的最长上升子序列长度为$f[i]$,则: $$f[i]=Max\{f[j]|j<i,h[j]\ge

bzoj 2244 [SDOI2011]拦截导弹(dp+CDQ+树状数组)

传送门 题解 看了半天完全没发现这东西和CDQ有什么关系…… 先把原序列翻转,求起来方便 然后把每一个位置表示成$(a,b,c)$其中$a$表示位置,$b$表示高度,$c$表示速度,求有多少个位置$a,b,c$都小于它,这就是一个三维偏序问题,直接CDQ就可以解决了…… 然后考虑如何求第二问,就是一个导弹所在的LIS数/总的LIS数,因为一个导弹的LIS必须包含自己,以$g[i]$表示以$i$结尾的LIS总数,不难发现有如下转移式 $$g[i]=\sum g[j] \{ (i<j,h[i]<h

洛谷 P2486 [SDOI2011]染色

题目描述 输入输出格式 输入格式: 输出格式: 对于每个询问操作,输出一行答案. 输入输出样例 输入样例#1: 6 5 2 2 1 2 1 1 1 2 1 3 2 4 2 5 2 6 Q 3 5 C 2 1 1 Q 3 5 C 5 1 2 Q 3 5 输出样例#1: 3 1 2 说明 题目大意:给一棵树,要求支持链覆盖+查询链上颜色段数 先考虑链上怎么做吧,颜色段数这个东西支持区间加,一个区间可以用三个属性表示:左端点的颜色,右端点的颜色,区间颜色段数 两段合并以后的颜色段数是:左段颜色段数+右

bzoj 2244: [SDOI2011]拦截导弹

1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define M 100009 5 using namespace std; 6 struct data 7 { 8 int x,y,z,f[2]; 9 double sum[2]; 10 }a[M],b[M]; 11 struct ss 12 { 13 int w; 14 double su; 15 }shu[M]; 16 int n,yy

BZOJ 2244 [SDOI2011]拦截导弹 ——CDQ分治

三维偏序,直接CDQ硬上. 正反两次CDQ统计结尾的方案数,最后统计即可. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i) #define D(i,j,k) for (int i=j;i>=i;--i) #define

BZOJ 2244: [SDOI2011]拦截导弹 [CDQ分治 树状数组]

传送门 题意:三维最长不上升子序列以及每个元素出现在最长不上升子序列的概率 $1A$了好开心 首先需要从左右各求一遍,长度就是$F[0][i]+F[1][i]-1$,次数就是$G[0][i]*G[1][i]$ 我们可以用一些转换来简化代码 反转之后变成$LIS$,然后再反转并且$x,y$取反还是$LIS$,写一遍就可以啦 然后本题的树状数组需要维护最大值以及最大值的数量,还有一个时间戳 #include <iostream> #include <cstdio> #include &