题目大意
题目给定点数
n
n
n和颜色数
m
m
m,分为三个问题:
1.给定两棵树,规定对于
u
,
v
u,v
u,v,若边
(
u
,
v
)
(u,v)
(u,v)同时在两棵树中出现,则
u
,
v
u,v
u,v必须染同种颜色。
2.给定一棵树,求对于所有第二棵树的可能出现情况,问题1的答案之和。
3.给定零棵树,求对于所有第一棵树的可能出现情况,问题2的答案之和。
题解
问题1
显然问题1是个SB题,如果两棵树中某条边重合,就直接缩起来,最后看有多少个缩起来之后的点即可。
问题2
问题2需要推一波式子。我们考虑计算至少有
k
k
k条边在两棵树之间重合的方案数,这样实际上会把原树分成
n
−
k
n-k
n−k个连通块。我们假设第
i
i
i个连通块的大小为
a
i
a_i
ai,于是可以使用Matrix-Tree定理算出生成树个数是下面行列式的值(令
m
=
n
−
k
m=n-k
m=n−k):
∣
(
n
−
a
1
)
a
1
−
a
1
a
2
−
a
1
a
3
⋯
−
a
1
a
m
−
1
−
a
2
a
1
(
n
−
a
2
)
a
2
−
a
2
a
3
⋯
−
a
2
a
m
−
1
−
a
3
a
1
−
a
3
a
2
(
n
−
a
3
)
a
3
⋯
−
a
3
a
m
−
1
⋯
⋯
⋯
⋯
⋯
−
a
m
−
1
a
1
−
a
m
−
1
a
2
−
a
m
−
1
a
3
⋯
(
n
−
a
m
−
1
)
a
m
−
1
∣
=
∏
i
=
1
m
−
1
a
i
∣
n
−
a
1
−
a
2
−
a
3
⋯
−
a
m
−
1
−
a
1
n
−
a
2
−
a
2
⋯
−
a
m
−
1
−
a
1
−
a
2
n
−
a
3
⋯
−
a
m
−
1
⋯
⋯
⋯
⋯
⋯
−
a
1
−
a
2
−
a
3
⋯
n
−
a
m
−
1
∣
=
∏
i
=
1
m
−
1
a
i
∣
n
0
0
⋯
−
n
0
n
0
⋯
−
n
0
0
n
⋯
−
n
⋯
⋯
⋯
⋯
⋯
0
0
0
⋯
n
−
∑
i
=
1
m
−
1
a
i
∣
=
n
m
−
2
∏
i
=
1
m
a
i
\left|\begin{matrix} (n-a_1)a_1 & -a_1a_2 & -a_1a_3 & \cdots & -a_1a_{m-1} \\ -a_2a_1 & (n-a_2)a_2 & -a_2a_3 & \cdots & -a_2a_{m-1} \\ -a_3a_1 & -a_3a_2 & (n-a_3)a_3 & \cdots & -a_3a_{m-1} \\ \cdots & \cdots & \cdots & \cdots & \cdots\\ -a_{m-1}a_1 & -a_{m-1}a_2 & -a_{m-1}a_3 & \cdots & (n-a_{m-1})a_{m-1} \end{matrix}\right| \\ =\prod_{i=1}^{m-1}a_i\left|\begin{matrix} n-a_1 & -a_2 & -a_3 & \cdots & -a_{m-1} \\ -a_1 & n-a_2 & -a_2 & \cdots & -a_{m-1} \\ -a_1 & -a_2 & n-a_3 & \cdots & -a_{m-1} \\ \cdots & \cdots & \cdots & \cdots & \cdots\\ -a_1 & -a_2 & -a_3 & \cdots & n-a_{m-1} \end{matrix}\right| \\ =\prod_{i=1}^{m-1}a_i\left|\begin{matrix} n & 0 & 0 & \cdots & -n \\ 0 & n & 0 & \cdots & -n \\ 0 & 0 & n & \cdots & -n \\ \cdots & \cdots & \cdots & \cdots & \cdots\\ 0 & 0 & 0 & \cdots & n-\sum_{i=1}^{m-1}a_i \end{matrix}\right| \\ =n^{m-2}\prod_{i=1}^ma_i
∣∣∣∣∣∣∣∣∣∣(n−a1)a1−a2a1−a3a1⋯−am−1a1−a1a2(n−a2)a2−a3a2⋯−am−1a2−a1a3−a2a3(n−a3)a3⋯−am−1a3⋯⋯⋯⋯⋯−a1am−1−a2am−1−a3am−1⋯(n−am−1)am−1∣∣∣∣∣∣∣∣∣∣=i=1∏m−1ai∣∣∣∣∣∣∣∣∣∣n−a1−a1−a1⋯−a1−a2n−a2−a2⋯−a2−a3−a2n−a3⋯−a3⋯⋯⋯⋯⋯−am−1−am−1−am−1⋯n−am−1∣∣∣∣∣∣∣∣∣∣=i=1∏m−1ai∣∣∣∣∣∣∣∣∣∣n00⋯00n0⋯000n⋯0⋯⋯⋯⋯⋯−n−n−n⋯n−∑i=1m−1ai∣∣∣∣∣∣∣∣∣∣=nm−2i=1∏mai
但是这样有可能会计算多,比如强制让某些边重合时,在连通块之间的边也重合了。如果真正重合的边集为
E
E
E,那么它会被在所有
S
⊆
E
S\subseteq E
S⊆E中被计算恰好一次。我们希望最终
E
E
E的贡献恰好为
y
n
−
∣
E
∣
y^{n-|E|}
yn−∣E∣,
y
n
y^n
yn可以提取出来最后乘,于是我们需要满足
∑
S
⊆
E
f
(
∣
S
∣
)
=
y
−
∣
E
∣
\sum_{S\subseteq E}f(|S|)=y^{-|E|}
∑S⊆Ef(∣S∣)=y−∣E∣,二项式反演可以得到:
f
(
k
)
=
∑
i
=
0
k
(
−
1
)
k
−
i
(
k
i
)
y
−
i
=
(
1
y
−
1
)
k
f(k)=\sum_{i=0}^k(-1)^{k-i}\binom kiy^{-i}=(\frac 1y-1)^k
f(k)=∑i=0k(−1)k−i(ik)y−i=(y1−1)k。
也就是说我们把树分成
m
m
m个连通块,第
i
i
i块的大小为
a
i
a_i
ai,那么它对答案的贡献就是
(
y
−
1
−
1
)
n
−
m
n
m
−
2
∏
i
=
1
m
a
i
(y^{-1}-1)^{n-m}n^{m-2}\prod_{i=1}^ma_i
(y−1−1)n−mnm−2∏i=1mai。
于是
O
(
n
2
)
O(n^2)
O(n2)的dp应该比较显然了,记一下根所在的连通块大小即可。
我们再考虑 ∏ a i \prod a_i ∏ai的组合意义,它相当于在每个连通块中选一个点的总方案数。于是令 f [ i ] f[i] f[i]表示 i i i所在的连通块中选了点的方案数, g [ i ] g[i] g[i]表示 i i i所在连通块中没有选点。于是就可以 O ( n ) O(n) O(n)的dp了。
问题3
有了问题2的推导,问题3的算式还是比较明显的。首先暴力枚举连通块划分求值:
∑
∑
i
=
1
m
a
i
=
n
n
!
∏
i
=
1
m
a
i
a
i
−
2
a
i
!
m
!
(
n
m
−
2
∏
i
=
1
m
a
i
)
2
(
y
−
1
−
1
)
n
−
m
=
y
n
(
y
−
1
−
1
)
n
n
!
n
4
∑
∑
i
=
1
m
a
i
=
n
(
n
2
(
y
−
1
−
1
)
−
1
)
m
m
!
∏
i
=
1
m
a
i
a
i
a
i
!
\sum_{\sum_{i=1}^ma_i=n}\frac{n!\prod_{i=1}^m\frac{a_i^{a_i-2}}{a_i!}}{m!}\left(n^{m-2}\prod_{i=1}^ma_i\right)^2(y^{-1}-1)^{n-m} \\ =\frac{y^n(y^{-1}-1)^nn!}{n^4}\sum_{\sum_{i=1}^ma_i=n}\frac{(n^2(y^{-1}-1)^{-1})^m}{m!}\prod_{i=1}^m\frac{a_i^{a_i}}{a_i!}
∑i=1mai=n∑m!n!∏i=1mai!aiai−2(nm−2i=1∏mai)2(y−1−1)n−m=n4yn(y−1−1)nn!∑i=1mai=n∑m!(n2(y−1−1)−1)mi=1∏mai!aiai
EGF的形式已经出来了。令
F
(
x
)
=
∑
i
i
i
!
x
i
F(x)=\sum \frac{i^i}{i!}x^i
F(x)=∑i!iixi,则上式为:
[
x
n
]
y
n
(
y
−
1
−
1
)
n
n
!
n
4
∑
m
≥
1
(
F
(
x
)
n
2
(
y
−
1
−
1
)
−
1
)
m
m
!
=
[
x
n
]
y
n
(
y
−
1
−
1
)
n
n
!
n
4
e
F
(
x
)
n
2
(
y
−
1
−
1
)
−
1
[x^n]\frac{y^n(y^{-1}-1)^nn!}{n^4}\sum_{m\ge 1}\frac{(F(x)n^2(y^{-1}-1)^{-1})^m}{m!} \\ =[x^n]\frac{y^n(y^{-1}-1)^nn!}{n^4}e^{F(x)n^2(y^{-1}-1)^{-1}}
[xn]n4yn(y−1−1)nn!m≥1∑m!(F(x)n2(y−1−1)−1)m=[xn]n4yn(y−1−1)nn!eF(x)n2(y−1−1)−1
因此直接多项式exp即可。复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
多项式板子太长就不放了qwq
int n, m, typ;
namespace Solve0 {
int par[MAXN], vis[MAXN];
set<pair<int, int> > ss;
int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); }
void solve() {
for (int i = 1; i <= n; i++) par[i] = i;
for (int i = 1; i < n; i++) {
int u, v; read(u, v);
ss.insert(make_pair(u, v));
ss.insert(make_pair(v, u));
}
for (int i = 1; i < n; i++) {
int u, v; read(u, v);
if (ss.find(make_pair(u, v)) != ss.end())
par[find(u)] = find(v);
}
ll res = 1;
for (int i = 1; i <= n; i++) if (!vis[find(i)])
vis[find(i)] = 1, res = res * m % MOD;
printf("%lld\n", res);
}
}
namespace Solve1 {
struct Edge { int to, next; } edge[MAXN];
int head[MAXN], tot;
ll f[MAXN], g[MAXN], invn, invm;
void addedge(int u, int v) {
edge[++tot] = (Edge) { v, head[u] };
head[u] = tot;
}
void dfs(int u, int fa) {
f[u] = g[u] = invn;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if (v == fa) continue;
dfs(v, u);
ll a = (f[u] * f[v] % MOD * n % MOD * n + (f[u] * g[v] + g[u] * f[v]) % MOD * n % MOD * (invm - 1)) % MOD;
ll b = (g[u] * f[v] % MOD * n % MOD * n + g[u] * g[v] % MOD * n % MOD * (invm - 1)) % MOD;
f[u] = a, g[u] = b;
}
}
void solve() {
for (int i = 1; i < n; i++) {
int u, v; read(u, v);
addedge(u, v), addedge(v, u);
}
invn = modpow(n, MOD - 2);
invm = modpow(m, MOD - 2);
dfs(1, 0);
printf("%lld\n", f[1] * modpow(m, n) % MOD);
}
}
namespace Solve2 {
ll fac[MAXN], rev[MAXN]; int A[MAXN], B[MAXN];
void solve() {
if (m == 1) { printf("%d\n", modpow(n, 2 * n - 4)); return; }
for (int i = fac[0] = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
rev[n] = modpow(fac[n], MOD - 2);
for (int i = n; i > 0; i--) rev[i - 1] = rev[i] * i % MOD;
ll invm = modpow(m, MOD - 2), iinvm = (ll)modpow(invm - 1, MOD - 2) * n % MOD * n % MOD;
for (int i = 1; i <= n; i++) A[i] = modpow(i, i) * rev[i] % MOD * iinvm % MOD;
get_exp(A, B, get_tpow(n + 1));
printf("%lld\n", (ll)B[n] * modpow(n, MOD - 5) % MOD * fac[n] % MOD * modpow(m, n) % MOD * modpow(invm - 1, n) % MOD);
}
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
read(n, m, typ);
if (typ == 0) Solve0::solve();
else if (typ == 1) Solve1::solve();
else Solve2::solve();
return 0;
}