【题解】[USACO19DEC]Tree Depth P
更好的体验尽在我的博客。
不用生成函数,目前比 rank2 快四倍。
首先这是一道不简单的数数思维题。
我们要统计对于所有排列的深度之和,直接做非常不方便。而数数题一般将条件化简,或找到等价的容易处理的条件。
这里求深度,等价于枚举一个点的祖先,它的祖先个数
如果
如果不考虑其它限制条件,先求有多少个排列恰好有
现在有
所以我们先跑 DP 求出
时间复杂度
#define M 46600
#define N 305
int n, k, f[2][M], s[M], g[M], sz, ans[N];
#define ck(x) (x >= P ? x - P : x)
void undo(int x){
memset(g, 0, sizeof(g));
int cur = P - 1; g[0] = 1;
rp(i, sz - x){
if(i > x)ad(cur, g[i - x - 1]);
su(cur, g[i] = ck(f[n & 1][i] + cur));
}
}
int main() {
read(n, k, P);
f[0][0] = 1;
rp(i, n){
s[0] = f[i & 1][0] = 1, sz += i - 1;
rp(j, sz){
f[i & 1][j] = s[j] = ck(s[j - 1] + f[1 & (i - 1)][j]);
if(j >= i)su(f[i & 1][j], s[j - i]);
}
}
rp(i, n)ans[i] = f[n & 1][k];
rp(t, n - 1){
undo(t);
rp(i, n - t){
if(t <= k)ad(ans[i], g[k - t]);
ad(ans[i + t], g[k]);
}
}
rp(i, n)printf("%d ", ans[i]); el;
return 0;
}