• Luogu P2612 [ZJOI2012]波浪


    题目
    我们考虑从(1)(n)把每个数放到序列里面去,以消掉绝对值。
    在最后的序列中,如果(i)的某一边是序列的边界,那么(i)会产生(0)的贡献。如果(i)的某一边是一个比(i)小的数,那么(i)会产生(i)的贡献。如果(i)的某一边是一个比(i)大的数,那么(i)会产生(-i)的贡献。
    当我们放(i)时,所有小于(i)的数都已经放进序列了,而大于(i)的还没放。
    那么最后的序列中(i)的左右两边的情况就等同于放(i)的时候(i)两边是否紧贴着某个数。
    所以我们可以把连通块(一个极大的连续的放了数的段)的情况和边界的情况放进dp的状态里。
    (f_{i,j,k,l})表示放了(i)个数,序列中存在(j)个连通块,总贡献为(k)(这一维需要平移(5000)),有(l)个边界已经被紧贴((l=0,1,2))的方案数。
    发现第一维(i)显然是可以滚掉的。
    那么有以下几个转移:
    (1.i)一边是边界,一边是空:(f_{j,k,l}(2-p)->f_{j+1,k-i,l+1})
    (2.i)一边是边界,一边是连通块:(f_{j,k,l}(2-p)->f_{j,k+i,l+1})
    (3.)两边都是连通块:(f_{j,k,l}(j-1)->f_{j-1,k+2i,l})
    (4.)两边都是空:(f_{j,k,l}(j+1-l)->f_{j+1,k-2i,l})
    (5.)一边是连通块,一边是空:(f_{j,k,l}(j*2-l)->f_{j,k,l})
    那么最后的答案就是(frac{sumlimits_{i=5000+m}^{10000}f_{1,i,2}}{n!})
    注意要数据类型分治。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=107,M=10007;
    namespace db{long double f[2][N][M][3];}
    namespace flt{__float128 f[2][N][M][3];}
    int min(int a,int b){return a<b? a:b;}
    int n,m,k;
    template<class T>void out(T ans)
    {
        cout<<"0.",ans*=10;
        for(int i=1;i<=k;++i) cout<<(int)(ans+(k==i)*0.5),ans=(ans-(int)ans)*10;
    }
    template<class T>void solve(T f[][N][M][3])
    {
        T ans=0;
        f[0][0][5000][0]=1;
        int g=0,i,j,k,p;
        for(i=1;i<=n;++i)
        {
            g^=1,memset(f[g],0,sizeof f[g]);
            for(j=0;j<=min(i-1,m);++j)
                for(k=0;k<=10000;++k)
                    for(p=0;p<=2;++p)
                        if(f[g^1][j][k][p])
    		    {
                            if(k>=2*i) f[g][j+1][k-2*i][p]+=f[g^1][j][k][p]*(j+1-p);
                            if(j) f[g][j][k][p]+=f[g^1][j][k][p]*(j*2-p);
                            if(j>=2&&k+2*i<=10000) f[g][j-1][k+2*i][p]+=f[g^1][j][k][p]*(j-1);
                            if(p^2)
    			{
                                if(k>=i) f[g][j+1][k-i][p+1]+=f[g^1][j][k][p]*(2-p);
                                if(j&&k+i<=10000) f[g][j][k+i][p+1]+=f[g^1][j][k][p]*(2-p);
                            }
                        }
        }
        for(i=m;i<=5000;++i) ans+=f[g][1][5000+i][2];
        for(i=1;i<=n;++i) ans/=i;
        out(ans);
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        if(k<=8) solve(db::f);
        else solve(flt::f);
    }
    
  • 相关阅读:
    物联网常见通信协议RFID、NFC、Bluetooth、ZigBee等梳理
    最全NB-IoT/eMTC物联网解决方案名录汇总
    解析:智慧医疗发展的5大趋势
    国家集成电路产业基金“二期”正在酝酿,规模直逼万亿
    Java基础【基本数据类型包装类、int与String 之间的相互转换】
    Java基础 【Arrays 类的使用】
    Java基础【冒泡、选择排序、二分查找】
    String、StringBuffer 的使用 ,两个面试问题
    使用Java Api 操作HDFS
    Linux下使用wget下载FTP服务器文件
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/11763549.html
Copyright © 2020-2023  润新知