【学习笔记】[AGC022F] Checkers
首先不考虑算重,因为这题坑点在于当 n ≥ 5 n\ge 5 n≥5时不同结构的树可能生成相同的结果。
那么我们考虑生成不同的系数序列 A A A,然后用可重集算一下方案数。考虑将 − 1 -1 −1的边缩去后所形成的树,第 i i i层的点表示的是 2 i 2^i 2i,那么如何知道每个节点的正负呢?

由瞪眼大法可知,如果 v v v是 u u u的儿子,那么 v v v会多乘一个系数 2 2 2,说明 v v v是中间点,那么假设 u u u有 s u s_u su个儿子, u u u的符号就是 ( − 1 ) s u (-1)^{s_u} (−1)su。同时如果 u u u有儿子的话,那么其中 ⌈ s u 2 ⌉ \lceil\frac{s_u}{2}\rceil ⌈2su⌉个儿子的符号会变成 u u u符号的相反数, ⌊ s u 2 ⌋ \lfloor\frac{s_u}{2}\rfloor ⌊2su⌋个儿子的符号会和 u u u相同。
据此,我们可以自上往下按层数 
     
      
       
       
         d 
        
       
         p 
        
       
      
        dp 
       
      
    dp。比较棘手的问题在于,它与上一层和下一层的状态都有关。这个坑点很隐蔽啊
然后我借鉴题解 设 
     
      
       
       
         d 
        
        
        
          p 
         
         
         
           i 
          
         
           , 
          
         
           j 
          
         
        
       
      
        dp_{i,j} 
       
      
    dpi,j表示 
     
      
       
       
         i 
        
       
      
        i 
       
      
    i个点形成的树,最下面一层有 
     
      
       
       
         j 
        
       
      
        j 
       
      
    j个节点有奇数个儿子节点的方案数。枚举下一层的点数 
     
      
       
       
         k 
        
       
      
        k 
       
      
    k,那么我们知道有 
     
      
       
       
         j 
        
       
      
        j 
       
      
    j个节点符号和父亲不同,剩下的一半相同,一半不同。我们再枚举实际与父亲相同的个数 
     
      
       
       
         p 
        
       
      
        p 
       
      
    p,那么下一层至少需要 
     
      
       
       
         ∣ 
        
        
         
         
           k 
          
         
           − 
          
         
           j 
          
         
        
          2 
         
        
       
         − 
        
       
         p 
        
       
         ∣ 
        
       
      
        |\frac{k-j}{2}-p| 
       
      
    ∣2k−j−p∣个有奇数个儿子,也就是可以转移到 
     
      
       
       
         d 
        
        
        
          p 
         
         
         
           i 
          
         
           + 
          
         
           k 
          
         
           , 
          
         
           ∣ 
          
          
           
           
             k 
            
           
             − 
            
           
             j 
            
           
          
            2 
           
          
         
           − 
          
         
           p 
          
         
           ∣ 
          
         
        
       
      
        dp_{i+k,|\frac{k-j}{2}-p|} 
       
      
    dpi+k,∣2k−j−p∣去。一个错误的想法是,同时转移到 
     
      
       
       
         d 
        
        
        
          p 
         
         
         
           i 
          
         
           + 
          
         
           k 
          
         
           , 
          
         
           ∣ 
          
          
           
           
             k 
            
           
             − 
            
           
             j 
            
           
          
            2 
           
          
         
           − 
          
         
           p 
          
         
           ∣ 
          
         
           + 
          
         
           2 
          
         
           w 
          
         
        
       
      
        dp_{i+k,|\frac{k-j}{2}-p|+2w} 
       
      
    dpi+k,∣2k−j−p∣+2w去,但是这样会算重,因为本质上这一层的状态是相同的。不难证明取最小值可以覆盖所有情况。
重新来看这道题,感觉还是可以给出一个为什么不重不漏的解释的,虽然这个解释非常别扭。考虑对于一个系数序列,对应若干个不同的树,每一个有一个儿子个数为奇数的点的数目,考虑取其中字典序最小的方案,并且要保证同一层为奇数的点全为 
     
      
       
       
         0 
        
       
      
        0 
       
      
    0或者 
     
      
       
       
         1 
        
       
      
        1 
       
      
    1。这样前面的那些转移都说的通了。可以自己试一下。但是我总感觉这是在强行解释,而且没有什么启发性。
复杂度 O ( n 4 ) O(n^4) O(n4)。
#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
const int mod=1e9+7;
int n;
ll dp[55][55],fac[55],inv[55];
ll fpow(ll x,ll y=mod-2){
	ll z(1);
	for(;y;y>>=1){
		if(y&1)z=z*x%mod;
		x=x*x%mod;
	}return z;
}
void init(int n){
	fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
	inv[n]=fpow(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
void add(ll &x,ll y){
	if((x+=y)>=mod)x-=mod;
}
ll binom(ll x,ll y){
	return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
    cin>>n;init(n);
    dp[1][0]=dp[1][1]=1;
    for(int i=1;i<n;i++){
    	for(int j=0;j<=i;j++){
    		for(int k=max(j,1);k<=n-i;k++){
    			if(k-j&1)continue;
    			for(int p=0;p<=k;p++){
    				add(dp[i+k][abs((k-j)/2-p)],dp[i][j]*inv[p]%mod*inv[k-p]%mod);
				}
			}
		}
	}
	cout<<dp[n][0]*fac[n]%mod;
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号