@computerkiller
2021-02-12T17:17:35.000000Z
字数 1664
阅读 570
数学
题解
这题超级没有素质。
连个题解都搜不到。
好不容易会做一点。
测了一下没过样例。
不过还好我有办法。
先容大块再斥小块。
三百的三次方飘过。
得到CE优异成绩。
哇哈哈哈哈哈哈哈。
原来是因为OJ炸了。
我已经会放过我了。
接下来该放代码了。
做法描述在代码前。
个点 每个点有一种颜色 问你 个点构成的无根树中 不存在一个大于 的同色的连通块
容斥好题
我们考虑可以钦定几个点构成连通块然后再钦定同色块内不允许
我们考虑怎么计算答案 显然有一个结论:
考虑后面的东西可以怎么计算显然是在统计块内答案的时候一起做了
设计这样一个 : 表示前 种颜色的点 分成 个集合 集合内的方案数和大小的乘积
显然有:
先考虑每个钦定的连通块内的方案数, 表示将 个点分成 个大小小于等于 的连通块的方案数 也就是不考虑块间连边的方案数:
我们再可以考虑把不合法的情况容斥掉去掉 那么我们变钦定大小为 同色的结点构成的集合内分成的 个连通块 枚举块之间连同色边的数量 然后乘上容斥系数 再顺便算乘上块的大小的积
预处理后面那个 然后就可以 预处理出来 了
signed main()
{
inv[1] = 1;
for (int i = 2; i < N; i++)
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
fac[0] = ifac[0] = 1;
for (int i = 1; i < N; i++)
fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
read(n,m);
for (int i = 1; i <= n; i++)
{
int x;
read(x);
t[x]++;
}
f[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
for (int k = 1; k <= i && k <= m; k++)
f[i][j] = (f[i][j] + ksm(k,k - 2) * k % mod * C(i - 1,k - 1) % mod * f[i - k][j - 1] % mod) % mod;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= i; j++)
{
if (j & 1) sum[i] = (sum[i] + f[i][j] * ksm(i,j - 1) % mod) % mod;
else sum[i] = (sum[i] - f[i][j] * ksm(i,j - 1) % mod + mod) % mod;
}
}
g[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
for (int k = 1; k <= i; k++)
g[i][j] = (g[i][j] + C(i - 1,k - 1) * g[i - k][j - 1] % mod * sum[k] % mod) % mod;
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= n; j++)
for (int k = 0; k <= t[i] && k <= j; k++)
dp[i][j] = (dp[i][j] + dp[i - 1][j - k] * g[t[i]][k] % mod) % mod;
int ans = 0;
for (int i = 1; i <= n; i++)
ans = (ans + dp[n][i] * ksm(n,i - 2) % mod) % mod;
writeln(ans);
return 0;
}