[AGC022F] Checkers 题解
考虑神秘建树。我们将所有 \(B\) 向 \(A\) 连边,就会又双叒叕形成一棵以最后剩下的那个点为根的有根树。我们考虑每个点对于根节点的影响。
首先,发现每个点对父亲的贡献一定是 \(2x^i\) 或 \(-2x^i\),所以传递到根节点时,贡献即为 \(c_i2^{d_i}x^i\),其中 \(c_i\in\{-1,1\},d_i\) 为 \(i\) 点的深度。由于 \(x\) 巨大,所以我们观察到本题关键结论:
两个方案最终答案不同 等价于 两个方案对应的 \(c_i,d_i\) 不同。
\(d_i\) 和树的形态有关,待会儿 \(dp\) 的时候再说,我们先进一步分析 \(c_i\)。
我们模拟一下一只点 \(u\) 的行进过程:
- 以他的儿子们为跳板,取反 \(sn_u\) 次。
- 被他的父亲当跳板,然后消失。
所以可知对于一个点 \(u\),恰好有 \(\lfloor\frac{sn_u}2\rfloor\) 个儿子被取反奇数次。所以得到:
- \(c_{rt}=(-1)^{sn_{rt}}\)
- 对于非叶子节点 \(u\),有 \(\lfloor\frac{sn_u}2\rfloor\) 个儿子节点满足 \(c_v=(-1)^{sn_v}c_u\),其余的儿子节点 \(w\) 满足 \(c_w=(-1)^{sn_v+1}c_u\)。
由于 \(d\) 的缘故,我们考虑一层一层的求答案,所以我们 \(dp\) 的第一维 \(i\) 表示当前枚举到树的第几层,第二维 \(j\) 表示当前放置了多少个点。
如何将 \(c\) 融入进来呢?考虑记 \(e_i=-[sn_i\bmod 2]c_i\),所以若对应 \(c_i\) 相等,则两棵树每层的 \(\sum e_i\) 也相等,所以 \(\sum[e_i=1]\) 和 \(\sum[e_i=-1]\) 也相等。这样,我们就可以将第三维 \(k\) 设为这一层 \(\sum e_i\) 的总和。
总结一下,我们设 \(f_{i,j,k}\) 表示枚举到第 \(i\) 层,放置了 \(j\) 个点,这一层的 \(\sum e_i=k\) 的总方案数。
那么我们先枚举该层的点数 \(t\),再设其中 \(h\) 个点的 \(c_i=1\),则有:
时间复杂度超高,\(O(n^5)\),不过够用了。
#include<bits/stdc++.h>
using namespace std;
const int N=55,p=1e9+7;
int n,dp[N][N][N*2],C[N][N],ans;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n,dp[1][1][n]=dp[1][1][n+1]=C[0][0]=1;
for(int i=0;i<=n;C[++i][0]=1)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%p;
for(int i=2;i<=n;i++) for(int j=i;j<=n;j++) for(int k=j-n;k<=n-j;k++)
for(int t=1;t<=j;t++) for(int c=0;c<=t;c++) if(abs(2*(k+c)-t)<=t)
dp[i][j][k+n]=(dp[i][j][k+n]+1ll*dp[i-1][j-t][2*(k+c)-t+n]*C[j][t]%p*C[t][c])%p;
for(int i=1;i<=n;i++) ans=(ans+dp[i][n][n])%p;
return cout<<ans,0;
}

浙公网安备 33010602011771号