洛谷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("\n");
    }
    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("\n");
}
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;
}
posted @ 2017-09-13 09:32  菜狗xzz  阅读(1003)  评论(0编辑  收藏  举报