zoukankan      html  css  js  c++  java
  • bzoj 2244: [SDOI2011]拦截导弹

    Description

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

    solution

    正解:CDQ+DP
    这题显然是一个三维偏序,考虑CDQ优化DP:
    偏序为:(i<j,x[i]>x[j],y[i]>y[j])
    首先我们按正常CDQ的套路分治 (x)
    考虑 (mid) 左边对右边的影响
    我们将 (x)按从大到小排序,按归并排序的方法每一次将左边的大于右边当前 (y) 的加入,以 (y) 为下标建立树状数组,那么大于当前 (y) 的都是可以转移到的,访问树状数组找出最大值即可
    对于第二问:
    (i) 被拦截的概率是 (i) 出现在的方案数目/方案总数,那么我们在维护第一问最大值时,也要顺便维护满足最大值的方案数,如何判断 (i) 是否在最优方案中?
    我们正反各做一边第一问的DP,答案分别为(f1,f2),如果(f1+f2-1==ans)即满足条件,将 (f1,f2) 对应的方案相乘即可
    注意:CDQ分治优化DP时,分治顺序要稍作调整,具体为:求出左边答案,拿左边更新完右边后再分治右边

    tips:这题方案数是可以爆long long的,注意开double存方案

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=50005;
    int n,b[N],num=0,tot;
    struct node{
        int x,y,id,p;
    }a[N];
    bool compx(node i,node j){return i.x>j.x;}
    bool compi(node i,node j){return i.p<j.p;}
    int f[2][N];double g[2][N];
    struct Bit{
        int x;double s;
    }tr[N];
    il void add(int sta,int to,double fa){
        for(RG int i=sta;i<=tot;i+=(i&(-i))){
            if(to>tr[i].x){tr[i].x=to,tr[i].s=fa;}
            else if(to==tr[i].x)tr[i].s+=fa;
        }
    }
    il Bit qry(int sta){
        Bit ret;ret.x=-1;
        for(RG int i=sta;i>=1;i-=(i&(-i)))
            if(tr[i].x && tr[i].x>ret.x)ret=tr[i];
            else if(tr[i].x==ret.x)ret.s+=tr[i].s;
        return ret;
    }
    il void Clear(int sta){
        for(RG int i=sta;i<=tot;i+=(i&(-i)))
            tr[i].s=tr[i].x=0;
    }
    il void solve(int l,int r,bool t){
        if(l==r)return ;
        int mid=(l+r)>>1;
        solve(l,mid,t);
        sort(a+l,a+mid+1,compx);
        sort(a+mid+1,a+r+1,compx);
        RG int xl=l,xr=mid+1,j;Bit tmp;
        while(xl<=mid && xr<=r){
            if(a[xl].x>=a[xr].x){
                add(tot-a[xl].y+1,f[t][a[xl].id],g[t][a[xl].id]);
                xl++;
            }
            else{
                j=a[xr].id;
                tmp=qry(tot-a[xr].y+1);xr++;
                if(tmp.x==-1)continue;
                if(tmp.x+1>f[t][j]){
                    g[t][j]=tmp.s;
                    f[t][j]=tmp.x+1;
                }
                else if(tmp.x+1==f[t][j])g[t][j]+=tmp.s;
            }
        }
        while(xr<=r){
            j=a[xr].id;
            tmp=qry(tot-a[xr].y+1);xr++;
            if(tmp.x==-1)continue;
            if(tmp.x+1>f[t][j]){
                g[t][j]=tmp.s;
                f[t][j]=tmp.x+1;
            }
            else if(tmp.x+1==f[t][j])g[t][j]+=tmp.s;
        }
        for(int i=l;i<=mid;i++)Clear(tot-a[i].y+1);
        sort(a+l,a+r+1,compi);
        solve(mid+1,r,t);
    }
    void work()
    {
        int maxnub=0;RG int i;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            scanf("%d%d",&a[i].x,&a[i].y),b[++num]=a[i].y,
                a[i].id=a[i].p=i,maxnub=Max(a[i].x,maxnub);
        sort(b+1,b+num+1);tot=unique(b+1,b+num+1)-b-1;
        for(i=1;i<=n;i++)
            a[i].y=lower_bound(b+1,b+tot+1,a[i].y)-b,
                f[0][i]=f[1][i]=g[1][i]=g[0][i]=1;
        solve(1,n,0);
    
        sort(a+1,a+n+1,compi);
        for(i=1;i<=n-i+1;i++)swap(a[i],a[n-i+1]);
        for(i=1;i<=n;i++)
            a[i].x=maxnub-a[i].x,a[i].y=tot-a[i].y+1,a[i].p=n-a[i].p+1;
        sort(a+1,a+n+1,compi);
        solve(1,n,1);
        int ans=0;double maxlen=0;
        for(i=1;i<=n;i++){
            if(f[0][i]>ans){
                ans=f[0][i];
                maxlen=g[0][i];
            }
            else if(ans==f[0][i])maxlen+=g[0][i];
        }
        printf("%d
    ",ans);
        for(i=1;i<=n;i++){
            if(f[0][i]+f[1][i]-1==ans)
                printf("%.5lf ",1.0*g[0][i]/maxlen*g[1][i]);
            else printf("0.00000 ");
        }
    }
    
    int main()
    {
        work();
        return 0;
    }
    
    
  • 相关阅读:
    “automation服务器不能创建对象”的问题的解决方案大全
    转载区分C#中的Abstract函数和Virtual函数
    DOS批处理
    数据库设计范式
    java 内存查看工具
    Java内存溢出详解
    Struts2 循环编辑指定次数
    Selenium 使用
    spring security 获取当前用户信息
    由MyEclipse内存不足谈谈JVM内存
  • 原文地址:https://www.cnblogs.com/Hxymmm/p/7705662.html
Copyright © 2011-2022 走看看