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;
}

原文地址:https://www.cnblogs.com/Hxymmm/p/7705662.html