zoukankan      html  css  js  c++  java
  • 洛谷2612&&bzoj2817 [ZJOI2012]波浪

    洛谷2612&&bzoj2817 [ZJOI2012]波浪


    原题链接


    题解

    因为有abs不太好搞,考虑拆掉abs.
    生成排列的方法之一:n个空位,从1到n一次插入一个空位。
    这样搞的话考虑一个数的贡献
    如果是233333 1 666666 |233333-1|+|1-666666|==233333+-1+666666-1 所以1的贡献为-2
    如果是233333 inf 666666 |233333-inf|+|inf-666666|==inf-233333+inf-666666 所以inf的贡献为2inf
    如果是1 2 3 |1-2|+|2-3|==2-1+3-2 所以2的贡献为0
    也就是说一个数的贡献为这个数×(-sgn(左边的数-这个数)-sgn(右边的数-这个数))
    (边界上的数可以看成这个数×(-sgn(相邻的数-这个数))
    然后因为是从小到大插的,所以只要看这个数和多少已插入的数相邻
    (f[i][j][k][l]:已经插入了i个点,共j段连续,前面数的总贡献为(k-5000),边界共放了l个的方案数)
    k的话从0~10000,C++党没负下标只好这样了,P党无所谓
    插入i分多种情况

    • 插入在边界,且与一段相连,贡献为i
    • 插入在边界,自成一段,贡献为-i
    • 插入不在边界,与一段相连,贡献为0(因为一边放了一边没放)
    • 插入不在边界,自成一段,贡献为-2i
    • 插入不在边界,而且这次插入使得两段相连,贡献为2i

    最后统计一下每种多少种情况即可。
    会写高精,__float128大法好
    k<=8用double


    Code

    // It is made by XZZ
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define rep(a,b,c) for(rg int a=b;a<=c;a++)
    #define drep(a,b,c) for(rg int a=b;a>=c;a--)
    #define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
    #define il inline
    #define rg register
    #define vd void
    typedef long long ll;
    il int gi(){
        rg int x=0;rg char ch=getchar();
        while(ch<'0'||ch>'9')ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x;
    }
    #define db __float128
    int n,m,K;
    namespace bigK{
        db f[2][101][10001][3];
        //f[a][b][c][d]
        //塞了a个点,共b段,总贡献为(c-5000),边界共放了d个的方案数
    #define F(a,b,c,d) (f[a][b][c+5000][d])
        il vd out(db ans,int k){
    	int tot=ans;printf("%d.",tot);
    	while(k--){
    	    ans=(ans-tot*1.0)*10.0;
    	    if(!k)ans=ans+0.5;
    	    tot=ans;printf("%d",tot);
    	}printf("
    ");
        }
        il vd main(){
    	int now=0;
    	db ans=0,s1=1.0;
    	F(0,0,0,0)=1.00;
    	rep(i,2,min(n,20))F(0,0,0,0)/=i*s1;
    	db tot;
    	rep(i,1,n){
    	    now^=1;memset(f[now],0,sizeof f[now]);
    	    rep(j,0,i)rep(k,-5000,5000)rep(l,0,2){
    		tot=F(now^1,j,k,l);
    		if(tot==0)continue;
    		if(l^2){
    		    if(j)F(now,j,k+i,l+1)+=tot*(2-l);//插入在边界 与一段相连
    		    F(now,j+1,k-i,l+1)+=tot*(2-l);//插入在边界 自成一段
    		}
    		if(j)F(now,j,k,l)+=tot*(j*2-l);//插入不在边界 与一段相连
    		F(now,j+1,k-i*2,l)+=tot*(j+1-l);//插入不在边界 自成一段
    		if(j>1)F(now,j-1,k+i*2,l)+=tot*(j-1);//插入不在边界 这次插入使得两段相连
    	    }
    	}
    	rep(i,m,5000)ans+=F(now,1,i,2)*s1;
    	rep(i,21,n)ans/=s1*i;
    	out(ans,K);
        }
    }
    double g[2][101][10001][3];
    il vd out(double ans,int k){
        int tot=ans;printf("%d.",tot);
        while(k--){
            ans=(ans-tot*1.0)*10.0;
            if(!k)ans=ans+0.5;
            tot=ans;printf("%d",tot);
        }printf("
    ");
    }
    int main(){
    #define Fname "wave"
        freopen(Fname".in","r",stdin);
        freopen(Fname".out","w",stdout);
        n=gi(),m=gi(),K=gi();
        if(K>8){bigK::main();}
    #define G(a,b,c,d) g[a][b][c+5000][d]
        else{
    	int now=0;
            double ans=0,s1=1.0;
    	G(0,0,0,0)=1.00;
    	double tot;
    	rep(i,1,n){
    	    now^=1;memset(g[now],0,sizeof g[now]);
    	    rep(j,0,i)rep(k,-5000,5000)rep(l,0,2){
    		tot=G(now^1,j,k,l);
    		if(tot==0)continue;
    		if(l^2){
    		    if(j)G(now,j,k+i,l+1)+=tot*(2-l);//插入在边界 与一段相连
    		    G(now,j+1,k-i,l+1)+=tot*(2-l);//插入在边界 自成一段
    		}
    		if(j)G(now,j,k,l)+=tot*(j*2-l);//插入不在边界 与一段相连
    		G(now,j+1,k-i*2,l)+=tot*(j+1-l);//插入不在边界 自成一段
    		if(j>1)G(now,j-1,k+i*2,l)+=tot*(j-1);//插入不在边界 这次插入使得两段相连
    	    }
    	}
    	rep(i,m,5000)ans+=G(now,1,i,2)*s1;
    	rep(i,1,n)ans/=s1*i;
            out(ans,K);
        }
        return 0;
    }
    
  • 相关阅读:
    C语言|博客作业02
    少走弯路的十条忠告
    怎么算是优秀的程序员写给工作2,3年了的同行
    .NET世界的M型化原文作者奚江华
    工作以后十不要 减少奋斗30年
    <转>[创业经验]程序员创业:我的软件推广成功之路
    一个程序员的C#命名规则<转>
    推荐奚江华著《圣殿祭祀ASP.NET 3.5 专家技术手册 C#篇及他的TW博客进入方法》
    C#算法
    使用 DataFormatString 属性来提供列中各项的自定义格式
  • 原文地址:https://www.cnblogs.com/xzz_233/p/7513366.html
Copyright © 2011-2022 走看看