[BZOJ2688]Green Hackenbush

[BZOJ2688]Green Hackenbush

题意: 有(n)棵随机的二叉树,每棵只知道大小为(a_i)

博弈:每次选取一个子树删掉,只剩根不能操作,求先手获胜概率

考虑这个博弈,求出一棵树的( ext{SG})

显然有:

1.只有一个点的树的( ext{SG})值为0

2.多个树组合的问题为( ext{SG})值异或

暴力(dp),对于树(T)求答案,设(T)所有可行的后继状态集合为(N(T)),则得到( ext{SG})值的表达式为

$ ext{SG}(T)= ext{mex}_{Rin N(T)}lbrace ext{SG(R)} brace $

直接求解复杂度过高,考虑归纳性质

性质:

1.一棵根节点只有一个儿子的树,其( ext{SG})值为儿子的( ext{SG})值+1

考虑归纳证明:

设子树为(T),令(T+u)表示(T)子树上面接上自己作为根,问题变为求证( ext{SG}(T+u)= ext{SG}(T)+1)

设已经归纳证明所有(T)的子联通块成立

我们要求( ext{SG}(T+u))

( ext{SG}(T+u)= ext{mex}{ ext{SG}(u),forall _{Rin N(T)} ext{SG}(R+u)})

由归纳的性质有

(forall _{Rsubsetneq T} ext{SG}(R+T)= ext{SG}(R)+1)

又因为( ext{SG}(u)=0),看做把所有儿子的情况平移了1,0的位置由自己占据,因而上式成立

2.多叉树的问题可以归纳为 根分别接上每个儿子得到的树 的问题的组合

因为儿子之间实际互不干扰,比较容易理解

由此得到,一棵树的( ext{SG})值为其所有儿子的( ext{SG})值+1的异或和

(dp_{n,i})为一棵(n)个节点的二叉树( ext{SG})值为(i)的概率,为了便于转移,设空树的( ext{SG})值为-1

考虑直接枚举两棵子树的大小和( ext{SG})

考虑对于(n)个节点的二叉树,设其左儿子为(i)时的总概率为(F_i)

得到的( ext{dp})转移是

(dp_{n,(a+1)oplus (b+1)}leftarrow {dp_{i,a}cdot dp_{n-i-1,b}cdot F_i})

我们知道(n)个节点的二叉树方案数为(Catalan(n)=frac{(2n)!}{n!(n+1)!})

由此得到$egin{aligned} F_i=frac{Catalan(i)Catalan(n-i-1)}{Catalan(n)}end{aligned} $

此题范围可以直接带入(Catalan(i))求解,但是依然要提一下递推的做法(似乎精度更有保障?)

$egin{aligned} F_i=frac{frac{(2i)!}{i!(i+1)!}cdot frac{(2n-i-2)!}{(n-i-1)!(n-i)!}}{frac{(2n)}{n!(n+1)!}}end{aligned} $

递推求解(F_i),每次(i)改变一阶乘只会改变1或者2,因此由(F_{i-1})得到(F_i)的递推式为

(F_i=left{ egin{aligned}frac{n(n+1)}{2n(2n-1)}&& i=0\ F_{i-1}cdot frac{2i(2i-1)}{(i+1)i}frac{(n-i+1)(n-i)}{2(n-i)(2n-2i-1)} && iin[1,n-1]end{aligned} ight.)

化简之后应该是

(F_i=left{ egin{aligned}frac{(n+1)}{2(2n-1)}&& i=0\ F_{i-1}cdot frac{(2i-1)}{(i+1)}frac{(n-i+1)}{(2n-2i-1)} && iin[1,n-1]end{aligned} ight.)

至此得到一个朴素的(O(n^4))预处理,由于是异或,可以用( ext{FWT}_{oplus})求解,复杂度为(O(n^3))

对于输入的每棵树,类似背包地叠加概率即可,复杂度为(O(n^3))

以下是朴素dp代码

#include<bits/stdc++.h>
using namespace std;
typedef double db;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=128;

int n;
db dp[N][N];

void FWT(db *a,int f){
	for(int i=1;i<N;i<<=1){
		for(int l=0;l<n;l+=i*2) {
			for(int j=l;j<l+i;j++){
				db t=a[j+i];
				a[j+i]=a[j]-t;
				a[j]+=t;
			}
		}
	}
	if(f==-1) rep(i,0,N-1) a[i]/=N;
}

db F[N],G[N];

int main(){
	dp[0][0]=1,dp[1][0]=1;
	rep(i,2,100) {
		F[0]=1.0/(2*i)/(2*i-1)*(i+1)*i;
		rep(j,1,i-1) {
			F[j]=F[j-1] *  (2*j)*(2*j-1)/(j+1)/j   * 1.0/(2*(i-j))/(2*(i-j)-1)*(i-j+1)*(i-j);
		}
		rep(a,0,i-1) rep(h1,0,N-1) if(dp[a][h1]>0) {
			rep(h2,0,N-1) if(dp[i-a-1][h2]) {
				int nxt=0;
				if(a>0) nxt^=h1+1;
				if(i-1-a>0) nxt^=h2+1;
				dp[i][nxt]+=dp[a][h1]*dp[i-a-1][h2]*F[a];
			}
		}
	}
	n=rd();
	rep(i,0,N-1) F[i]=0;
	F[0]=1;
	rep(i,1,n) {
		int x=rd();
		rep(j,0,N-1) G[j]=0;
		rep(j,0,N-1) if(F[j]) rep(k,0,N-1) G[j^k]+=F[j]*dp[x][k];
		rep(j,0,N-1) F[j]=G[j];
	}
	db ans=0;
	rep(i,1,N-1) ans+=F[i];
	printf("%.6lf
",ans);
}


以下是FWT优化代码

#include<bits/stdc++.h>
using namespace std;
typedef double db;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=128;

int n;
db dp[N][N],T[N][N];

void FWT(db *a,int f){
	for(int i=1;i<N;i<<=1){
		for(int l=0;l<N;l+=i*2) {
			for(int j=l;j<l+i;j++){
				db t=a[j+i];
				a[j+i]=a[j]-t;
				a[j]+=t;
			}
		}
	}
	if(f==-1) rep(i,0,N-1) a[i]/=N;
}

db F[N],G[N];

int main(){
	dp[0][0]=1,dp[1][0]=1;
	T[0][0]=1,T[1][1]=1;
	FWT(T[0],1),FWT(T[1],1);

	rep(i,2,100) {
		F[0]=1.0/(2*i)/(2*i-1)*(i+1)*i;
		rep(j,1,i-1) {
			F[j]=F[j-1] *  (2*j)*(2*j-1)/(j+1)/j   * 1.0/(2*(i-j))/(2*(i-j)-1)*(i-j+1)*(i-j);
		}
		rep(j,0,i-1) rep(k,0,N-1) dp[i][k]+=T[j][k]*T[i-j-1][k]*F[j];
		FWT(dp[i],-1);
		rep(j,0,N-2) T[i][j+1]=dp[i][j];
		FWT(T[i],1);
	}
	n=rd();
	rep(i,0,N-1) F[i]=0;
	F[0]=1;
	rep(i,1,n) {
		int x=rd();
		rep(j,0,N-1) G[j]=0;
		rep(j,0,N-1) if(F[j]) rep(k,0,N-1) G[j^k]+=F[j]*dp[x][k];
		rep(j,0,N-1) F[j]=G[j];
	}
	db ans=0;
	rep(i,1,N-1) ans+=F[i];
	printf("%.6lf
",ans);
}







原文地址:https://www.cnblogs.com/chasedeath/p/13615832.html