bzoj 2244: [SDOI2011]拦截导弹

题意:

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

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

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

题解:

容易发现,假如按最长上升子序列的思想,明显是三维偏序。注意这里要先分治左边,然后合并再分治右边。
然后如果想再算方案的话似乎就是四维偏序了,但其实只要求f的时候在树状数组中多存一个量,表方案数,就可以了。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
    int op,x,y,c;
}q[50010],tmp[50010];int cnt=0;
struct NODE {int h,v,num;}a[50010];
int n,h[50010],v[50010],f[4][50010],Q[50010];
double g[4][50010];
struct trnode{
    int max;
    double sum;
}tr[50010];
int lowbit(int x) {return x&-x;}
void rechange(int k) {for(int i=k;i<=n;i+=lowbit(i)) tr[i].max=tr[i].sum=0;}
void change(int k,int c,double s)
{
    for(int i=k;i<=n;i+=lowbit(i))
    {
        if(tr[i].max==c) tr[i].sum+=s;
        if(tr[i].max<c) tr[i].max=c,tr[i].sum=s;
    }
}
trnode get(int k)
{
    trnode ans;ans.max=0;ans.sum=0;
    for(int i=k;i>=1;i-=lowbit(i))
    {
        if(tr[i].max==ans.max) ans.sum+=tr[i].sum;
        if(ans.max<tr[i].max) ans=tr[i];
    }
    return ans;
}
bool cmp_h(NODE a,NODE b) {return a.h<b.h;}
bool cmp_v(NODE a,NODE b) {return a.v<b.v;}
bool cmp1(node a,node b) {return a.x<b.x;}
void cdq(int l,int r,int op)
{
    if(l==r) return;
    int mid=(l+r)/2;
    cdq(l,mid,op);
    for(int i=mid+1;i<=r;i++) tmp[i]=q[i];
    sort(tmp+mid+1,tmp+r+1,cmp1);
    int i=l,j=mid+1,st=0,len=0;
    while(i<=mid&&j<=r)
    {
        if(q[i].x<=tmp[j].x) change(q[i].y,f[op][q[i].c],g[op][q[i].c]),Q[++st]=q[i].y,i++;
        else
        {
            trnode ans=get(tmp[j].y);
            if(ans.max<f[op][tmp[j].c]-1) {j++;continue;}
            else if(ans.max==f[op][tmp[j].c]-1) g[op][tmp[j].c]+=ans.sum;
            else f[op][tmp[j].c]=ans.max+1,g[op][tmp[j].c]=ans.sum;
            j++;
        }
    }
    while(j<=r)
    {
        trnode ans=get(tmp[j].y);
        if(ans.max<f[op][tmp[j].c]-1) {j++;continue;}
        else if(ans.max==f[op][tmp[j].c]-1) g[op][tmp[j].c]+=ans.sum;
        else f[op][tmp[j].c]=ans.max+1,g[op][tmp[j].c]=ans.sum;
        j++;
    }
    for(i=1;i<=st;i++) rechange(Q[i]);
    cdq(mid+1,r,op);
    i=l;j=mid+1;len=0;
    while(i<=mid&&j<=r)
    {
        if(q[i].x<=q[j].x) tmp[++len]=q[i++];
        else tmp[++len]=q[j++];
    }
    while(i<=mid) tmp[++len]=q[i++];
    while(j<=r) tmp[++len]=q[j++];
    for(i=1;i<=len;i++) q[l+i-1]=tmp[i];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d %d",&a[i].h,&a[i].v),a[i].num=i;
    for(int i=1;i<=n;i++) f[1][i]=f[2][i]=1,g[1][i]=g[2][i]=1;
    sort(a+1,a+n+1,cmp_h);
    int TMP=0;
    for(int i=1;i<=n;i++)
    {
        if(i==1||a[i].h!=a[i-1].h) TMP++;
        h[a[i].num]=TMP;
    }
    sort(a+1,a+n+1,cmp_v);
    TMP=0;
    for(int i=1;i<=n;i++)
    {
        if(i==1||a[i].v!=a[i-1].v) TMP++;
        v[a[i].num]=TMP;
    }
    for(int i=1;i<=n;i++) q[n-i+1].x=h[i],q[n-i+1].y=v[i],q[n-i+1].c=i;
    cdq(1,n,1);
    for(int i=1;i<=n;i++) q[i].x=n-h[i]+1,q[i].y=n-v[i]+1,q[i].c=i;
    cdq(1,n,2);
    int max=0;double sum=0;
    for(int i=1;i<=n;i++)
    {
        if(max==f[1][i]) sum+=g[1][i];
        if(max<f[1][i]) max=f[1][i],sum=g[1][i];
    }
    printf("%d\n",max);
    for(int i=1;i<=n;i++)
    {
        if(f[1][i]+f[2][i]-1!=max) printf("0.00000 ");
        else printf("%.5lf ",g[1][i]*g[2][i]/sum);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值