0%

[WC2019] 数树

时隔 \(9\) 个月,终于肝出这题了

设恰好有 \(i\) 条边不重复的方案数为 \(f_i\)

\[ f_i = \sum_{|E|=i}\sum_{E'\subseteq E}(-1)^{i-|E'|}n^{|E'|-1}\prod_{S=C(E')}s \] 交换求和次序得

\[ f_i = \sum_{E'}(-1)^{i-|E'|}n^{|E'|-1}\prod_{S=C(E')}s\sum_{|E|=i,E'\subseteq E} \]

化简得

\[ f_i = \sum_{E'}(-1)^{i-|E'|}n^{|E'|-1}\prod_{S=C(E')}s\binom{n-1-|E'|}{i-|E'|} \]

\[ \mathcal{F_i}=\sum_{|E|=i}\prod_{S=C(E)}s \]

则有

\[ f_i = \sum_{j=0}^i(-1)^{i-j}n^{j-1}\binom{n-1-j}{i-j}\mathcal{F_j} \]

答案为

\[ \sum_{i=0}^{n-1}\sum_{j=0}^i(-1)^{i-j}n^{j-1}\binom{n-1-j}{i-j}\mathcal{F_j}y^i \]

交换求和次序得

\[ \sum_{j=0}^{n-1}n^{j-1}\mathcal{F_j}\sum_{i=j}^{n-1}(-1)^{i-j}\binom{n-1-j}{i-j}y^{i+1} \]

化简为

\[ \sum_{j=0}^{n-1}n^{j-1}\mathcal{F_j}\sum_{i=0}^{n-1-j}y^{i+j+1}(-1)^{i}\binom{n-1-j}{i} \]

用二项式定理化简得

\[ \sum_{j=0}^{n-1}n^{j-1}y^{j+1}(1-y)^{n-1-j}\mathcal{F_j} \]

代入得

\[ \sum_{j=0}^{n-1}n^{j-1}y^{j+1}(1-y)^{n-1-j}\sum_{|E|=j}\prod_{S=C(E)}s \]

那么有

\[ \frac{(1-y)^n}{n^2}\sum_{j=0}^{n-1}n^{j+1}(\frac{y}{1-y})^{j+1}\sum_{|E|=j}\prod_{S=C(E)}s \]

进一步得

\[ \frac{(1-y)^{n}}{n^2}\sum_{E}\prod_{S=C(E)}\frac{ny}{1-y}s \]

后面部分可以使用背包完成,时间复杂度为 \(O(n^2)\)

\(f_n\) 为该联通块根节点所在联通块大小为 \(n\) 的方案数,\(k=\frac{1-y}{ny}\) 则在合并两个联通块时有

\[ h_n=k\sum_{i=0}^nf_ig_{n-i}(\frac{1}{i}+\frac{1}{n-i})+f_n\sum_{i=0}^Ng_i \]

注意到我们最后只需关注 \(\sum_{i=0}^Nf_i\) 并结合母函数的技巧启发我们考虑幂级数

\[ f(x)=\sum_{i=0}^Nf_ix^i \]

最后答案即为 \(f(1)\)

那么套在 DP 上就有

\[ \sum_{n=0}^Nh_nx^n=k\sum_{n=0}^N\sum_{i=0}^nf_ig_{n-i}(\frac{1}{i}+\frac{1}{n-i})x^n+\sum_{n=0}^Nf_n\sum_{i=0}^Ng_ix^n \]

化简为

\[ h(x)=k((\sum_{i=0}^n\frac{f_n}{n}x^n)g(x)+(\sum_{i=0}^n\frac{g_n}{n}x^n)f(x))+g(1)f(x) \]

考虑函数

\[ \hat{f}(x)=\sum_{i=0}^n\frac{f_i}{i}x^i \]

则有

\[ h(x)=k(\hat{f}(x)g(x)+\hat{g}(x)f(x))+f(x)g(1) \]

\(x=1\) 得到

\[ h(1)=k(\hat{f}(1)g(1)+\hat{g}(1)f(1))+f(1)g(1) \]

考虑 \(\hat{f}(x)\) 的求法

\[ \sum_{n=0}^N\frac{h_n}{n}x^n=k\sum_{n=0}^N\frac{1}{n}\sum_{i=0}^nf_ig_{n-i}(\frac{1}{i}+\frac{1}{n-i})x^n+\sum_{n=0}^N\frac{f_n}{n}\sum_{i=0}^Ng_ix^n \]

化简为

\[ \hat{h}(x)=k\hat{f}(x)\hat{g}(x)+\hat{f}(x)g(1) \]

同样

\[ \hat{h}(1)=k\hat{f}(1)\hat{g}(1)+\hat{f}(1)g(1) \]

这样我们就得到了 \(O(n)\)DP

考虑 op3 的做法,答案为

\[ \sum_{T}\frac{(1-y)^{n}}{n^2}\sum_{E\subseteq T}\prod_{S=C(E)}\frac{ny}{1-y}s \]

prufer 序列枚举联通块得

\[ \frac{(1-y)^{n}}{n^4}\sum_{\sum_{i=1}^ms_i=n}\frac{1}{m!}\frac{n!}{\prod_{i=1}^ms_i!}\prod_{i=1}^ms_i^{s_i-2}\prod_{i=1}^m\frac{n^2y}{1-y}{s_i}^2 \]

考后面部分的 EGF 得,并设 \(w=\frac{n^2y}{1-y}\)

\[ [x^n]f=\sum_{\sum_{i=1}^ms_i=n}\frac{1}{m!}\prod_{i=1}^m\frac{ws_i^{s_i}}{s_i!} \]

这启发我们考虑函数 \(g=\sum_{i}w\frac{i^i}{i!}x^i\)

带入有

\[ f=\sum_m\frac{g^m}{m!} \]

用等比数列求和化简得

\[ f=e^g \]

最终答案即为

\[ \frac{n!(1-y)^{n}}{n^4}[x^n]f \]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <map>
#include <vector>
typedef long long int64;
using namespace std;
inline int read(int f = 1, int x = 0, char ch = ' ')
{
while(!isdigit(ch = getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) x = x*10+ch-'0', ch = getchar();
return f*x;
}
const int N = 8e5+5, P = 998244353;
int n, y, op, ans;
int64 qpow(int64 a, int b)
{
int64 ret = 1;
for( ; b; b >>= 1, a = a*a%P) if(b&1) ret = a*ret%P;
return ret;
}
namespace solve0
{
map<pair<int, int>, int> rec;
void sol()
{
for(int i = 2; i <= n; ++i)
{
int x = read(), y = read();
if(x > y) swap(x, y); ++rec[make_pair(x, y)];
}
for(int i = 2; i <= n; ++i)
{
int x = read(), y = read();
if(x > y) swap(x, y); ans += rec[make_pair(x, y)];
}
ans = qpow(y, n-ans);
}
}
namespace solve1
{
struct Edge
{
int next, to;
Edge(int next = 0, int to = 0):next(next), to(to) {};
}edge[N<<1];
int head[N], tot;
void _add(int x, int y) { edge[++tot] = Edge(head[x], y), head[x] = tot; }
void add(int x, int y) { _add(x, y), _add(y, x); }
int f[N], g[N], inv[N], w, invw;
void dfs(int x, int fa)
{
f[x] = g[x] = w;
for(int i = head[x]; i; i = edge[i].next)
{
int y = edge[i].to; if(y == fa) continue;
dfs(y, x);
f[x] = ((1ll*g[x]*f[y]%P+1ll*f[x]*g[y]%P)%P*invw%P+1ll*f[x]*f[y]%P)%P;
g[x] = (1ll*g[y]*invw%P+f[y])%P*g[x]%P;
}
}
void sol()
{
if(y == 1) return ans = qpow(n, n-2)%P, void(0);
else inv[1] = 1, w = 1ll*n*y%P*qpow(P+1-y, P-2)%P, invw = qpow(w, P-2);
for(int i = 2; i <= n; ++i)
{
int x = read(), y = read();
add(x, y), inv[i] = P-1ll*(P/i)*inv[P%i]%P;
}
dfs(1, 0);
ans = f[1]*qpow(P+1-y, n)%P*inv[n]%P*inv[n]%P;
}
}
namespace solve2
{
struct Poly
{
vector<int> _;
inline int& operator [] (int i) { return _[i]; }
inline int ti() { return _.size()-1; }
inline void set(int ti) { _.resize(ti+1); }
}F;
int64 w[2][N], inv[N], ifac[N], fac[N], k;
int lim;
void prepare(int ti)
{
for(lim = 1; lim <= ti; lim <<= 1);
w[0][0] = w[0][lim] = w[1][0] = w[1][lim] = 1;
int64 g = qpow(3, (P-1)/lim);
for(int i = 1; i < lim; ++i)
w[1][lim-i] = w[0][i] = w[0][i-1]*g%P;
}
void NTT(Poly &A, int t)
{
if(!t) A.set(lim-1);
for(int i = 0, j = 0; i < lim; ++i)
{
if(i > j) A[i] ^= A[j] ^= A[i] ^= A[j];
for(int k = lim>>1; (j ^= k) < k; k >>= 1);
}
for(int mid = 1; mid < lim; mid <<= 1)
for(int len = mid<<1, j = 0; j < lim; j += len)
for(int k = 0, p = 0, q = lim/len; k < mid; ++k, p += q)
{
int64 x = A[j+k], y = w[t][p]*A[j+k+mid]%P;
A[j+k] = x+y<P?x+y:x+y-P;
A[j+k+mid] = x-y<0?x-y+P:x-y;
}
if(!t) return; int64 v = inv[lim];
for(int i = 0; i < lim; ++i) A[i] = A[i]*v%P;
}
Poly operator * (Poly A, Poly B)
{
int n = A.ti(), m = B.ti(); prepare(n+m);
NTT(A, 0), NTT(B, 0);
for(int i = 0; i < lim; ++i) A[i] = 1ll*A[i]*B[i]%P;
NTT(A, 1); A.set(n+m);
return A;
}
Poly operator - (Poly A, Poly B)
{
int n = A.ti(), m = B.ti(); A.set(max(n, m));
for(int i = 0; i <= m; ++i) A[i] = A[i]-B[i]<0?A[i]-B[i]+P:A[i]-B[i];
return A;
}
Poly polyInv(Poly A, int n)
{
Poly B; B.set(n-1), A.set(n-1);
if(n == 1) return B[0] = qpow(A[0], P-2), B;
B = polyInv(A, (n+1)>>1), prepare(n<<1);
NTT(B, 0), NTT(A, 0);
for(int i = 0; i < lim; ++i) B[i] = (2ll-1ll*A[i]*B[i]%P+P)%P*B[i]%P;
NTT(B, 1), B.set(n-1);
return B;
}
Poly polyDer(Poly A, int n)
{
for(int i = 0; i < n-1; ++i) A[i] = 1ll*(i+1)*A[i+1]%P; A[n-1] = 0;
return A;
}
Poly polyInte(Poly A, int n)
{
for(int i = n-1; i; --i) A[i] = inv[i]*A[i-1]%P; A[0] = 0;
return A;
}
Poly polyLn(Poly A, int n) { return polyInte(polyDer(A, n)*polyInv(A, n), n); }
Poly polyExp(Poly A, int n)
{
Poly B; B.set(n-1), A.set(n-1);
if(n == 1) return B[0] = 1, B;
B = polyExp(A, (n+1)>>1), B.set(n-1), ++A[0];
B = B*(A-polyLn(B, n)); B.set(n-1);
return B;
}
void sol()
{
if(y == 1) return ans = qpow(1ll*n*n%P, n-2), void(0);
fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = 1, k = y*qpow(P+1-y, P-2)%P*n%P*n%P, F.set(n);
for(int i = 2; i <= n<<3; ++i) inv[i] = P-(P/i)*inv[P%i]%P, fac[i] = fac[i-1]*i%P, ifac[i] = ifac[i-1]*inv[i]%P;
for(int i = 1; i <= n; ++i) F[i] = k*qpow(i, i)%P*ifac[i]%P;
F = polyExp(F, n+1), ans = F[n]*qpow(P+1-y, n)%P*qpow(inv[n], 4)%P*fac[n]%P;
}
}
int main()
{
n = read(), y = read(), op = read();
if(op == 0) solve0::sol();
else if(op == 1) solve1::sol();
else solve2::sol();
printf("%d\n", ans);
return 0;
}