bzoj2244 [SDOI2011]拦截导弹(CDQ+dp||二维树状数组)

题目链接

分析:
这个导弹拦截好像不大一样
有高度有速度(还有数组下标)的限制

一个三维偏序的模型,可以用CDQ分治解决,复杂度 O(nlog2n) O ( n l o g 2 n )

但是这样只能求得LIS
而每个导弹被拦截的概率取决于ta存在于多少LIS中
ans=LISLIS a n s = 每 个 元 素 所 在 的 L I S 个 数 总 L I S 个 数

考虑更改一下dp
f[i] f [ i ] 表示以 i i 为结尾的最长非升子序列长度
g[i] g [ i ] 表示以 i i 为结尾的最长非升子序列方案数
h[i] h [ i ] 表示以 i i 为开头的最长非升子序列长度
s[i] s [ i ] 表示以 i i 为开头的最长非升子序列方案数

然后对于每一个 i i ,判断 f[i]+h[i]1 f [ i ] + h [ i ] − 1 是否就是最长答案
如果是,那么 g[i]s[i] g [ i ] ∗ s [ i ] 就是这个点出现的次数

用两遍 CDQ C D Q 维护上述四个值

tip

前辈好像都表示这道题就卡精度的风险
所以都用double即可

为什么会狂WA不止???
不知道,造了大数据跑了跑,也没什么问题
bzoj2244 [SDOI2011]拦截导弹(CDQ+dp||二维树状数组)_第1张图片
要是改,代码就又要面目全非了
(好不容易自己码风的CDQ,不想改了)

没错,想copy代码的同学肯定GG了
不过代码还是可以用来对拍的
b(bao)z(zou)oj。。。orz

#include
#include
#include
#include
#include

using namespace std;

const double eps=1e-8;
const int N=100010;
struct node{
    int h,v,id,pos;
};
node a[N],b[N],p[N];
int n,V[N];
double f[N],g[N],h[N],s[N],c[N],d[N];
double maxf,maxg;

void hash() {
    for (int i=1;i<=n;i++) V[i]=a[i].h;
    sort(V+1,V+1+n);
    int nn=unique(V+1,V+1+n)-V-1;
    for (int i=1;i<=n;i++) a[i].h=lower_bound(V+1,V+1+nn,a[i].h)-V;

    for (int i=1;i<=n;i++) V[i]=a[i].v;
    sort(V+1,V+1+n);
    nn=unique(V+1,V+1+n)-V-1;
    for (int i=1;i<=n;i++) a[i].v=lower_bound(V+1,V+1+nn,a[i].v)-V;
}

int cmp0(const node &a,const node &b) {
    return a.posint cmp1(const node &a,const node &b) {
    return (a.h>b.h)||((a.h==b.h)&&(a.v>b.v));
}

int cmp2(const node &a,const node &b) {
    return (a.hvoid add(int x,double z1,double z2) {
    for (int i=x;i<=n;i+=(i&(-i)))
        if (c[i]else if (c[i]==z1) d[i]+=z2;
}

void ask(int x) {
    maxf=0; maxg=0;
    for (int i=x;i>0;i-=(i&(-i)))
        if (c[i]>maxf)
            maxf=c[i],maxg=d[i];
        else if (c[i]==maxf) maxg+=d[i];
}

void clear(int x) {
    for (int i=x;i<=n;i+=(i&(-i)))
        c[i]=0,d[i]=0;
}

void CDQ_a(int l,int r) {         //以i结尾的最长下降子序列 
    if (l==r) return;
    int mid=(l+r)>>1;
    CDQ_a(l,mid);
    int cnt=0;
    for (int i=l;i<=r;i++) p[++cnt]=a[i];          //copy一下 不影响x坐标 
    sort(p+1,p+1+cnt,cmp1);                        //按照h排序(从大到小)
    for (int i=1;i<=cnt;i++)                       //树状数组维护v 
        if (p[i].pos<=mid) 
            add(n-p[i].v+1,f[p[i].id],g[p[i].id]);
        else {
            ask(n-p[i].v+1);
            if (f[p[i].id]1.0) {
                f[p[i].id]=maxf+1.0;
                g[p[i].id]=maxg;
            }
            else if (f[p[i].id]==maxf+1.0)
                g[p[i].id]+=maxg;
        }
    for (int i=1;i<=cnt;i++)
        if (p[i].pos<=mid) clear(n-p[i].v+1);
    CDQ_a(mid+1,r);
}

void CDQ_b(int l,int r) {        //以i开头的最长下降子序列
    if (l==r) return;
    int mid=(l+r)>>1;
    CDQ_b(l,mid);
    int cnt=0;
    for (int i=l;i<=r;i++) p[++cnt]=b[i];
    sort(p+1,p+1+cnt,cmp2);
    for (int i=1;i<=cnt;i++)
        if (p[i].pos<=mid) 
            add(p[i].v,h[p[i].id],s[p[i].id]);
        else {
            ask(p[i].v);
            if (h[p[i].id]1.0) {
                h[p[i].id]=maxf+1.0;
                s[p[i].id]=maxg;
            }
            else if (h[p[i].id]==maxf+1.0)
                s[p[i].id]+=maxg;
        }
    for (int i=1;i<=cnt;i++)
        if (p[i].pos<=mid) clear(p[i].v);
    CDQ_b(mid+1,r);
}

int main() 
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) {      //正向 
        scanf("%d%d",&a[i].h,&a[i].v);
        a[i].id=i; a[i].pos=i;
    }
    hash();
    for (int i=1;i<=n;i++) {      //逆向 
        b[i]=a[i]; b[i].pos=n-i+1;
    }

    sort(a+1,a+1+n,cmp0);
    for (int i=1;i<=n;i++) f[i]=1.0,g[i]=1.0;
    CDQ_a(1,n);

    sort(b+1,b+1+n,cmp0);
    for (int i=1;i<=n;i++) h[i]=1.0,s[i]=1.0;
    CDQ_b(1,n);

    double ans=0,tot=0;
    for (int i=1;i<=n;i++) ans=max(ans,f[i]);
    for (int i=1;i<=n;i++) if (f[i]==ans) tot+=g[i];
    printf("%.0lf\n",ans);
    for (int i=1;i<=n;i++)
        if (f[i]+h[i]-1==ans)
            printf("%.5lf%c",g[i]*s[i]/tot," \n"[i==n]);
        else printf("%.5lf%c",0.0," \n"[i==n]);
    return 0;
}

你可能感兴趣的:(dp,CDQ分治)