bzoj 2244: [SDOI2011]拦截导弹

题意

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

题解

很明显的一个CDQ分治DP。。
f[i]表示以他结尾的最多可以拦截多少个
这个用一个CDQ分治很快就可以算出来了
但是有多少种方案怎么弄呢?
其实我们可以在作CDQ的时候顺便弄出一个东西,就是这个答案优多少种方案
这个在树状数组上面加多一个辅助数组就可以了。。
然后经过他的怎么算呢?
我们可以倒着再做一个,求出后面开始,以他为开头的,其他一样,看做g[i]
如果一个导弹, f[i]+g[i]1=ans ,那么说明他是在上面的,用哪个辅助的数组统计答案就可以了

需要注意的地方:
1.在分治的时候,注意id的排序,因为我是分开的,前做左边,然后合并最后右边,所以会出现右边的id不顺序的情况
2.方案数乘积会爆 longlong ,要用 double

CODE:(调试都没有删。。)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
typedef long double LD;
const LL N=50005*2;
LL n;
struct qq
{
    LL h,v,id;
}s[N],k[N];
LD f[N],h[N];
LD F[N],H[N];//对应的方案数 
bool cmph (qq a,qq b){return a.h<b.h;}
bool cmpv (qq a,qq b){return a.v<b.v;}
bool cmpi (qq a,qq b){return a.id<b.id;}
LD g[N],G[N];
LL sta[N],top=0;
void LSH()
{
    LL Last,Cnt;
    for (LL u=1;u<=n;u++) k[u]=s[u];
    sort(k+1,k+1+n,cmph);
    Last=0;Cnt=0;
    for (LL u=n;u>=1;u--)
    {
        if (k[u].h!=Last)
        {
            Last=k[u].h;
            Cnt++;
        }
        s[k[u].id].h=Cnt;
    }

    for (LL u=1;u<=n;u++) k[u]=s[u];
    sort(k+1,k+1+n,cmpv);
    Last=0;Cnt=0;
    for (LL u=n;u>=1;u--)
    {
        if (k[u].v!=Last)
        {
            Last=k[u].v;
            Cnt++;
        }
        s[k[u].id].v=Cnt;
    }
}
LL lb (LL x){return x&(-x);}
LD shen;//个数 
LL get (LL x)
{
    LD lalal=0;
    while (x>=1)
    {
        if (g[x]>lalal) lalal=g[x],shen=0;
        if (g[x]==lalal)    shen=shen+G[x];
        x-=lb(x);
    }
    return lalal;
}
void change (LL x,LL y,LD z)
{
    while (x<=n)
    {
        if (g[x]<y) g[x]=y,G[x]=0;
        if (g[x]==y) G[x]+=z;
        x+=lb(x);
    }
}
void reset (LL x)//这个位置清空 
{
    while (x<=n)
    {
        g[x]=0;G[x]=0;
        x+=lb(x);
    }
}
void cdq (LL l,LL r)
{
    if (l==r)   return ;
/*  printf("OZY:%lld %lld\n",l,r);
    for (LL u=l;u<=r;u++) printf("%lld ",s[u].id);
        system("pause");*/
    LL mid=(l+r)>>1;
    sort(s+l,s+r+1,cmpi);
    cdq(l,mid);
    sort(s+l,s+1+mid,cmph);
    sort(s+1+mid,s+r+1,cmph);
    LL now=l;
//  memset(g,0,sizeof(g));
//  memset(G,0,sizeof(G));
    top=0;
    for (LL u=mid+1;u<=r;u++)//求解
    {
        while (now<=mid&&s[now].h<=s[u].h)
        {
            sta[++top]=s[now].v;
            change(s[now].v,f[s[now].id],F[s[now].id]);
            now++;
        }
        shen=0;
        LL lalal=get(s[u].v)+1;
        if (f[s[u].id]<lalal)   f[s[u].id]=lalal,F[s[u].id]=0;
        if (f[s[u].id]==lalal) F[s[u].id]+=shen;
    }
    for (LL u=1;u<=top;u++) reset(sta[u]);
    cdq(mid+1,r);
/*  for (LL u=1;u<=n;u++) printf("%lld ",f[u]);
    printf("\n");*/

    return ;
}
void cdq1 (LL l,LL r)
{
    if (l==r)   return ;
/*  printf("OZY:%lld %lld\n",l,r);
    for (LL u=l;u<=r;u++) printf("%lld ",s[u].id);
        system("pause");*/
    LL mid=(l+r)>>1;
    sort(s+l,s+r+1,cmpi);
    cdq1(mid+1,r);
    sort(s+l,s+1+mid,cmph);sort(s+1+mid,s+r+1,cmph);
    LL now=mid+1;
//  memset(g,0,sizeof(g));
//  memset(G,0,sizeof(G));
    top=0;
    for (LL u=l;u<=mid;u++)//求解? 
    {
        while (now<=r&&s[now].h<=s[u].h)
        {
                sta[++top]=s[now].v;
            change(s[now].v,h[s[now].id],H[s[now].id]);
            now++;
        }
        shen=0;
        LL lalal=get(s[u].v)+1;
        if (h[s[u].id]<lalal)  h[s[u].id]=lalal,H[s[u].id]=0;
        if (h[s[u].id]==lalal) H[s[u].id]+=shen;
    }
    for (LL u=1;u<=top;u++) reset(sta[u]);
    cdq1(l,mid);
    return ;
}
int main()
{
    scanf("%lld",&n);
    for (LL u=1;u<=n;u++)   
    {
        scanf("%lld%lld",&s[u].h,&s[u].v);  
        s[u].id=u;
        f[u]=1;F[u]=1;H[u]=1;h[u]=1;
    }
    LSH();
    sort(s+1,s+1+n,cmpi);
    cdq(1,n);
    LD ans=0;
    for (LL u=1;u<=n;u++) ans=max(ans,f[u]);
/*  for (LL u=1;u<=n;u++) 
    {
        printf("%lld:%lld %lld\n",u,f[u],F[u]);
    }
    printf("\n");*/
    printf("%.0Lf\n",ans);
    sort(s+1,s+1+n,cmpi);
    LSH();
    sort(s+1,s+1+n,cmpi);
    cdq1(1,n);
    LD tot=0;//一共有多少种方案
    for (LL u=1;u<=n;u++)
        if (f[u]==ans)
            tot=tot+F[u];
//  printf("%lld\n",tot);
    for (LL u=1;u<=n;u++)
    {
        if (f[u]+h[u]-1==ans)
        {
            printf("%.5Lf ",F[u]/tot*H[u]);
        }
        else printf("0.00000 ");
    }
/*  printf("\n");
    printf("f:");
    for (LL u=1;u<=n;u++) printf("%lld ",f[u]);
/*  printf("\nF:");
    for (LL u=1;u<=n;u++) printf("%lld ",F[u]);*/
/*  printf("\nh:");
    for (LL u=1;u<=n;u++) printf("%lld ",h[u]);
/*  printf("\nH:");
    for (LL u=1;u<=n;u++) printf("%lld ",H[u]);*/
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值