【学习笔记】[AGC022F] Checkers

首先不考虑算重,因为这题坑点在于当 n ≥ 5 n\ge 5 n5时不同结构的树可能生成相同的结果。

那么我们考虑生成不同的系数序列 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| 2kjp个有奇数个儿子,也就是可以转移到 d p i + k , ∣ k − j 2 − p ∣ dp_{i+k,|\frac{k-j}{2}-p|} dpi+k,2kjp去。一个错误的想法是,同时转移到 d p i + k , ∣ k − j 2 − p ∣ + 2 w dp_{i+k,|\frac{k-j}{2}-p|+2w} dpi+k,2kjp+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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值