zoukankan      html  css  js  c++  java
  • [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);
    }
    
    
    
    
    
    
    
    
  • 相关阅读:
    [导入]mootools框架【二】Core篇: 主要方法测试实例
    公司招聘中不能说的秘密 【转载】
    国外10个ASP.Net C#下的开源CMS
    [导入][Flash开发笔记] List控件删除指定label或data的项
    [导入]用C#截取指定长度的中英文混合字符串 改进版
    今天小侄子出生,想了一天的名字
    一个正则表达式的解释
    今日小收获
    昨天的事
    两点东西
  • 原文地址:https://www.cnblogs.com/chasedeath/p/13615832.html
Copyright © 2011-2022 走看看