zoukankan      html  css  js  c++  java
  • 机房测试:sort(归并+概率期望dp)

    题目:

     

     分析:

    定义dp[ i ] [ j ]为原序列中第i个元素,在归并后放在了j这个位置的概率

    最后的答案是概率乘上每一个可能的位置。

    考虑怎么转移:

    在归并排序中,遇到相同的就将对应的区间提出来,模拟两两相同元素比较的过程,统计贡献。

    对于上一层的一个元素k,它通过一堆相同的比较后,放入位置 t 的概率是:C(t-1,k-1)/2^t

    它原来位于位置k,就有k-1个元素在它前面,而它放入了位置t,要比较t次,所以是2^t

    左边放的k-1个,总共放了t-1个,所以有C(t-1,k-1)的可能。

    最后的答案就是枚举每一个元素的可能的位置以及期望去统计即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define N 505
    #define mid ((l+r)>>1)
    #define ri register int
    const ll mod = 998244353;
    ll fac[N],invfac[N],inv2[N],f[N],dp[N][N],ans[N];
    int a[N],b[N],c[N],n;
    ll quick_pow(ll a,ll k)
    {
        ll anss=1;
        while(k) { if(k&1) anss=anss*a %mod; a=a*a %mod; k>>=1; }
        return anss;
    }
    void init()
    {
        fac[0]=1;
        for(ri i=1;i<=n;++i) fac[i]=fac[i-1]*i %mod;
        invfac[n]=quick_pow(fac[n],mod-2);
        for(ri i=n;i>=1;--i) invfac[i-1]=invfac[i]*i %mod;
        inv2[1]=quick_pow(2,mod-2); inv2[0]=1;
        for(ri i=2;i<=n;++i) inv2[i]=inv2[i-1]*inv2[1] %mod;
    }
    ll C(int n,int m)
    {
        if(n<m) return 0;
        return fac[n]*invfac[n-m] %mod *invfac[m] %mod;
    }
    void work(int l1,int r1,int l2,int r2)//统计概率 
    {
        for(ri j=l1;j<=r1;++j){//枚举左区间每一个点统计贡献 
            for(ri k=1;k<=r1-l1+1;++k){//枚举左边的第k个元素放入t这个位置 
                for(ri t=k;t<=r2-l2+k;++t)//枚举这个点k放在下一个序列里面的位置t 
                f[t]=(f[t] + dp[b[j]][k]*C(t-1,k-1) %mod *inv2[t] %mod) %mod;//计算:C(t-1,k-1)/2^t 
                //特殊处理左边的全部放入,右边只能顺着放的情况,那么右边顺着放就不会产生贡献 
                for(ri t=r2-l2+1;t<=r2-l2+k;++t)//这里的t枚举的是右边全部放完 最后一个位置放的地方 
                //因为只有知道它放的地方 才知道从哪里开始已经不产生贡献 
                f[r2-l2+1+k]=( f[r2-l2+1+k] + dp[b[j]][k]*C(t-1,r2-l2) %mod *inv2[t] %mod) %mod;
                //因为k固定,右区间放多少元素也固定 所以这里的r2-l2+1+k是固定的 
            }
            for(ri k=1;k<=r1-l1+1+r2-l2+1;++k)
            dp[b[j]][k]=f[k],f[k]=0;//用f临时记录,可以减少一维dp,减少了层数那一维 
        }
        
        for(ri j=l2;j<=r2;++j){//和上面的一样 
            for(ri k=1;k<=r2-l2+1;++k){
                for(ri t=k; t<=r1-l1+k; ++t)
                f[t]=(f[t] + dp[b[j]][k]*C(t-1,k-1) %mod *inv2[t] %mod ) %mod;
                
                for(ri t=r1-l1+1; t<=r1-l1+k; ++t)
                f[r1-l1+1+k]=( f[r1-l1+1+k] + dp[b[j]][k]*C(t-1,r1-l1) %mod *inv2[t] %mod ) %mod;
            }
            for(ri k=1;k<=r1-l1+1+r2-l2+1;++k)
            dp[b[j]][k]=f[k],f[k]=0;
        }
    }
    void merge_sort(int l,int r)
    {
        if(l==r) { dp[b[l]][1]=1; return ; }//自己在自己本来的位置 概率是1 
        merge_sort(l,mid); merge_sort(mid+1,r);
        int p=l,q=mid+1;
        for(ri i=l;i<=r;++i){
            if(p<=mid && a[b[p]]<a[b[q]] || q>r) c[i]=b[p++];
            else if(q<=r && a[b[p]]>a[b[q]] || p>mid) c[i]=b[q++];
            else{
                int l1=p,r1=p,l2=q,r2=q;
                while(r1<mid && a[b[r1+1]]==a[b[l1]]) r1++;
                while(r2<r && a[b[r2+1]]==a[b[l2]]) r2++;
                work(l1,r1,l2,r2);//找到左边连续的一段 和 右边连续的一段 当合并他们的时候要统计贡献 
                for(ri k=l1;k<=r1;++k) c[i]=b[k], i++;
                for(ri k=l2;k<=r2;++k) c[i]=b[k], i++;
                i--;
                p=r1+1; q=r2+1;
            }
        }
        for(ri i=l;i<=r;++i) b[i]=c[i];
    }
    int main()
    {
        freopen("sort.in","r",stdin);
        freopen("sort.out","w",stdout);
        scanf("%d",&n);
        for(ri i=1;i<=n;++i) scanf("%d",&a[i]),b[i]=i;//不能对a直接排序,将a的下标b拿去排序 
        init();
        merge_sort(1,n);
        for(ri l=1,r;l<=n;l=r+1){
            r=l;
            while(r<n && a[b[r+1]]==a[b[l]]) r++;//跳连续的相同区间统计贡献 
            for(ri i=l;i<=r;++i)//这一段相同的里面 每一个数都有其可以放的位置 位置再乘上概率就是期望 
             for(ri j=1;j<=r-l+1;++j)
              ans[b[i]]=( ans[b[i]] + dp[b[i]][j]*(j+l-1) %mod ) %mod;//bi是i这个元素 在原来数组中的下标 这时的dp是最后一层的dp 
        }
        for(ri i=1;i<=n;++i) printf("%lld ",ans[i]);
        return 0;
    }
    View Code
  • 相关阅读:
    IntelliJ IDEA 14.03 java 中文文本处理中的编码格式设置
    应聘感悟
    STL string分析
    CUDA SDK VolumeRender 分析 (1)
    BSP
    CUDA SDK VolumeRender 分析 (3)
    CUDA SDK VolumeRender 分析 (2)
    Windows软件发布时遇到的一些问题
    Ten Commandments of Egoless Programming (转载)
    复习下光照知识
  • 原文地址:https://www.cnblogs.com/mowanying/p/11822860.html
Copyright © 2011-2022 走看看