zoukankan      html  css  js  c++  java
  • bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

    http://www.lydsy.com/JudgeOnline/problem.php?id=2244

    每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数

    pre_len [i] 表示以i结尾的最长不下降子序列的长度

    pre_sum[i] 表示对应长度下的方案数

    suf_len[i] 表示以i开头的最长不下降子序列长度

    suf_sum[i] 表示对应长度下的方案数

    若已有了这4个数组

    设最长上升子序列长度=mx

    那么 如果pre_len[i]+suf_len[i] - 1==mx,那么第i枚导弹在最长上升子序列上

    而且在pre_sum[i]*suf_sum[i] 个最长上升子序列上

    如何获取这四个数组?

    这是一个三维偏序问题,CDQ分治

    若i后面可以接j

    第一维:i的出现时间<j的出现时间

    第二维:i的高度>=j的高度

    第三维:i的速度>=j的速度

    具体实现:

    排序第一维,CDQ过程中排序解决第二维,树状数组解决第三维

    对于第三维,要离散化

    树状数组查询的是<=查询位置的信息,即用它来求最长不下降子序列更方便

    所以在解决以i为开头的最长不上升子序列的时候

    将第二维取负,第三维i变成n-i+1 

    在解决以i为开始的最长不上升子序列的时候

    将第一维取负

    对CDQ分治更进一步的理解:

    之前写CDQ分治做数据结构题的时候,

    写的是先解决左边对右边的贡献,在递归两边

    但是这道题,要先递归左边,再解决左边对右边的贡献,再递归右边

    因为最长不下降子序列 左边更新右边的时候,左边的所有不是等价的

    即也需要用左边更新之后的结果来更新右边

    而像那种修改查询题,左边的修改对左边查询的影响和对右边查询的影响是等价的

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    #define N 50001
    
    #define lowbit(x) ( x&-x )
    
    int n;
    int h[N],v[N];
    int tot,has[N];
    
    struct node
    {
        int x,y,z;
        int len;
        double sum;
    }e[N];
    
    int c[N];
    double d[N];
    
    int pre_len[N],suf_len[N];
    double pre_sum[N],suf_sum[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    bool cmdy(node p,node q)
    {
        if(p.y==q.y) return p.z<q.z;
        return p.y<q.y;
    }
    
    bool cmdx(node p,node q)
    {
        return p.x<q.x;
    }
    
    void change(int pos,int len,double sum)
    {
        while(pos<=n)
        {
            if(len>c[pos])
            {
                c[pos]=len;
                d[pos]=sum;
            }
            else if(len==c[pos]) d[pos]+=sum;
            pos+=lowbit(pos);
        }
    }
    
    int query_len(int pos)
    {
        int ans=0;
        while(pos)
        {
            ans= ans>=c[pos] ? ans : c[pos];
            pos-=lowbit(pos);
        }
        return ans;
    }
    
    double query_sum(int pos,int val)
    {
        double ans=0;
        while(pos)
        {
            if(c[pos]==val) ans+=d[pos];
            pos-=lowbit(pos);
        }
        return ans;
    }
    
    void clear(int pos)
    {
        while(pos<=n)
        {
            c[pos]=d[pos]=0;
            pos+=lowbit(pos);
        }
    }
    
    void cdq(int l,int r)
    {
        if(l==r) return;
        int mid=l+r>>1;
        cdq(l,mid);
        sort(e+l,e+mid+1,cmdy);
        sort(e+mid+1,e+r+1,cmdy);
        int i=l,j=mid+1;
        int len; double sum;
        for(;j<=r;++j)
        {
            while(i<=mid && e[i].y<=e[j].y)
            {
                change(e[i].z,e[i].len,e[i].sum);
                i++;
            }
            len=query_len(e[j].z)+1;
            sum=query_sum(e[j].z,len-1);
            if(len>e[j].len)
            {
                e[j].len=len;
                e[j].sum=sum;
            }
            else if(len==e[j].len) e[j].sum+=sum;
        }
        for(int k=l;k<=mid;++k) clear(e[k].z);
        sort(e+mid+1,e+r+1,cmdx);
        cdq(mid+1,r);
    }
    
    void work()
    {
        sort(has+1,has+n+1);
        tot=unique(has+1,has+n+1)-has-1;
        for(int i=1;i<=n;++i) v[i]=lower_bound(has+1,has+tot+1,v[i])-has;
        for(int i=1;i<=n;++i)
        {
            e[i].x=i;
            e[i].y=-h[i];
            e[i].z=tot-v[i]+1;
            e[i].len=e[i].sum=1;
        }
        cdq(1,n);
        for(int i=1;i<=n;++i) pre_len[e[i].x]=e[i].len,pre_sum[e[i].x]=e[i].sum;
        for(int i=1;i<=n;++i)
        {
            e[i].x=-i;
            e[i].y=h[i];
            e[i].z=v[i];
            e[i].len=e[i].sum=1;
        }
        sort(e+1,e+n+1,cmdx);
        cdq(1,n);
        for(int i=1;i<=n;++i) suf_len[-e[i].x]=e[i].len,suf_sum[-e[i].x]=e[i].sum;
    }
    
    void get_ans()
    {
        int mx=0;
        for(int i=1;i<=n;++i) mx=max(mx,pre_len[i]);
        printf("%d
    ",mx);
        double cnt=0;
        for(int i=1;i<=n;++i)
            if(pre_len[i]==mx) cnt+=pre_sum[i];
        for(int i=1;i<=n;++i)
            if(pre_len[i]+suf_len[i]-1==mx) printf("%.5lf ",(double)pre_sum[i]*suf_sum[i]/cnt);
            else printf("0 ");
    }
    
    int main()
    {
        read(n);
        for(int i=1;i<=n;++i) 
        {
            read(h[i]); read(v[i]);
            has[i]=v[i];
        }
        work();
        get_ans();
    }
  • 相关阅读:
    [转载]HashSet的存储机制
    Java基础加强
    [转载]JDK的动态代理深入解析(Proxy,InvocationHandler)
    Java语言基础Html
    Java语言基础JavaScript
    多线程数据与控制同步
    Expression Tree Basics表达式树基础
    调试优化利器ASP.NET 跟踪
    css position relative ,absolute ,float
    .net date /日期格式化
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8605562.html
Copyright © 2011-2022 走看看