loj6496. 「雅礼集训 2018 Day1」仙人掌
分析
首先考虑树的情况。用
f
[
u
]
[
0
/
1
]
f[u][0/1]
f[u][0/1]表示当前子树根的度数
≤
a
i
,
<
a
i
\le a_i, <a_i
≤ai,<ai的答案。
f
[
u
]
[
0
]
=
∏
k
1
+
k
2
+
⋯
k
n
≤
a
i
f
[
v
i
]
[
1
−
k
i
]
f[u][0]=\prod_{k_1+k_2+\cdots k_n\le a_i}f[v_i][1-k_i]
f[u][0]=k1+k2+⋯kn≤ai∏f[vi][1−ki]
f
[
u
]
[
1
]
=
∏
k
1
+
k
2
+
⋯
k
n
<
a
i
f
[
v
i
]
[
1
−
k
i
]
f[u][1]=\prod_{k_1+k_2+\cdots k_n<a_i}f[v_i][1-k_i]
f[u][1]=k1+k2+⋯kn<ai∏f[vi][1−ki]
这个东西生产函数
+
N
T
T
+NTT
+NTT一下就是一个
l
o
g
log
log的
仙人掌的话把环单独拉出来搞。
建圆方树,把信息放在方点上维护。由于是一个环的形式,所以某个方点可能对父亲圆点的度数最多产生
2
2
2的贡献,于是用
f
[
u
]
[
0
/
1
/
2
]
f[u][0/1/2]
f[u][0/1/2]。来维护。
这个时候考虑环上的转移,用
g
[
i
]
[
0
/
1
]
g[i][0/1]
g[i][0/1]表示环上搞到第
i
i
i个节点,其中
(
i
,
i
+
1
)
(i,i+1)
(i,i+1)这条边是否对
i
+
1
i+1
i+1有贡献的方案数。
方程
g
[
i
+
1
]
[
0
]
=
g
[
i
]
[
0
]
⋅
f
[
v
]
[
1
]
+
g
[
i
]
[
1
]
⋅
f
[
v
]
[
2
]
g[i+1][0]=g[i][0]\cdot f[v][1]+g[i][1] \cdot f[v][2]
g[i+1][0]=g[i][0]⋅f[v][1]+g[i][1]⋅f[v][2]
g
[
i
+
1
]
[
1
]
=
g
[
i
]
[
0
]
⋅
f
[
v
]
[
0
]
+
g
[
i
]
[
1
]
⋅
f
[
v
]
[
1
]
g[i+1][1]=g[i][0]\cdot f[v][0]+g[i][1] \cdot f[v][1]
g[i+1][1]=g[i][0]⋅f[v][0]+g[i][1]⋅f[v][1]
考虑当前点的度数即可。
这样的话多处理一下度数
<
a
i
−
1
<a_i-1
<ai−1的答案即可。
树上的
F
F
T
FFT
FFT种类遇到的也就两种,一种套路是点分治
+
F
F
T
+FFT
+FFT,一般考虑过根路径和子树转移,另一种就是关于儿子的转移,用边来分析复杂度。本题就是后者的典型。
代码
#include<bits/stdc++.h>
const int Nt = 524288, N = 2e5 + 10, P = 998244353;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
struct Edge {
int pr[N], to[N << 1], nx[N << 1], tp;
Edge() {tp = 1;}
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
}G, T;
int g[N][2], f[N][3], dfn[N], low[N], st[N], a[N], R[Nt], w[Nt], A[Nt], B[Nt], L, IvL, tp, tm, n, m, tot;
int Pow(int x, int k) {
int r = 1;
for(;k; x = 1LL * x * x % P, k >>= 1)
if(k & 1)
r = 1LL * r * x % P;
return r;
}
int Inv(int x) {return Pow(x, P - 2);}
int fixu(int a) {return a >= P ? a - P : a;}
int fixd(int a) {return a < 0 ? a + P : a;}
void Up(int &a, int b) {a = fixu(a + b);}
void Pre(int m) {
int x = 0; L = 1;
for(;(L <<= 1) < m; ++x) ;
for(int i = 1;i < L; ++i)
R[i] = R[i >> 1] >> 1 | (i & 1) << x;
int wn = Pow(3, (P - 1) / L); w[0] = 1;
for(int i = 1;i < L; ++i)
w[i] = 1LL * w[i - 1] * wn % P;
IvL = Inv(L);
}
void NTT(int *F) {
for(int i = 0;i < L; ++i)
if(i < R[i])
std::swap(F[i], F[R[i]]);
for(int i = 1, d = L >> 1; i < L; i <<= 1, d >>= 1)
for(int j = 0;j < L; j += i << 1) {
int *l = F + j, *r = F + j + i, *p = w, tp;
for(int k = i; k--; ++l, ++r, p += d)
tp = 1LL * *p * *r % P, *r = fixd(*l - tp), *l = fixu(*l + tp);
}
}
typedef std::vector<int> VI;
VI p[N];
void Get(int *A, const VI a, int L) {
for(int i = 0;i < a.size(); ++i)
A[i] = a[i];
for(int i = a.size();i < L; ++i)
A[i] = 0;
}
VI operator * (VI a, VI b) {
int m = a.size() + b.size() - 1;
VI c; Pre(m);
Get(A, a, L); Get(B, b, L);
NTT(A); NTT(B);
for(int i = 0;i < L; ++i)
A[i] = 1LL * A[i] * B[i] % P;
NTT(A);
for(int i = 0;i < m; ++i)
c.push_back(1LL * A[L - i & L - 1] * IvL % P);
return c;
}
void Tarjan(int u, int p) {
dfn[u] = low[u] = ++tm; st[++tp] = u;
for(int i = G.pr[u], v; i;i = G.nx[i])
if(i ^ p ^ 1) {
if(!dfn[v = G.to[i]]) {
Tarjan(v, i);
low[u] = std::min(low[u], low[v]);
if(dfn[u] == low[v])
for(T.add(u, ++tot);st[tp + 1] != v;)
T.add(tot, st[tp--]);
else if(low[v] > dfn[u])
T.add(u, v), --tp;
}
else low[u] = std::min(low[u], dfn[v]);
}
}
VI Prod(int L, int R) {
if(L == R) return p[L]; int m = L + R >> 1;
return Prod(L, m) * Prod(m + 1, R);
}
void Dfs(int u) {
for(int i = T.pr[u]; i; i = T.nx[i])
Dfs(T.to[i]);
if(u <= n) {
if(!T.pr[u]) {
f[u][0] = 1;
f[u][1] = (a[u] >= 1);
f[u][2] = (a[u] >= 2);
return ;
}
tp = 0;
for(int i = T.pr[u], v; i; i = T.nx[i]) {
p[++tp].clear(); v = T.to[i];
if(v > n) p[tp].push_back(f[v][2]);
p[tp].push_back(f[v][1]);
p[tp].push_back(f[v][0]);
}
VI c = Prod(1, tp);
Get(A, c, a[u] + 1);
for(int i = 1;i <= a[u]; ++i)
Up(A[i], A[i - 1]);
f[u][0] = A[a[u]];
if(a[u] >= 1) f[u][1] = A[a[u] - 1];
if(a[u] >= 2) f[u][2] = A[a[u] - 2];
}
else {
for(int fi = 0; fi <= 1; ++fi) {
g[0][fi] = 1; g[0][!fi] = 0; int j = 0;
for(int i = T.pr[u], v; i; i = T.nx[i], ++j) {
v = T.to[i];
g[j + 1][0] = (1LL * g[j][0] * f[v][1] + 1LL * g[j][1] * f[v][2]) % P;
g[j + 1][1] = (1LL * g[j][0] * f[v][0] + 1LL * g[j][1] * f[v][1]) % P;
}
Up(f[u][fi + 1], g[j][0]);
Up(f[u][fi], g[j][1]);
}
}
}
int main() {
tot = n = ri(); m = ri();
for(int i = 1;i <= m; ++i)
G.adds(ri(), ri());
for(int i = 1;i <= n; ++i)
a[i] = ri();
Tarjan(1, 0);
Dfs(1);
printf("%d\n", f[1][0]);
return 0;
}