WC2019数树
- 题目链接:WC2019数树
- 题意简述:直接看链接吧,简述不来.
- \(solution\)就是直接推式子,重点讲\(Sub1\)
- \(Sub1\):设红树边集为\(E\).
\[\begin{aligned}
ans &= \sum_S y^{n-|S\cap E|} \\
&= y^n\sum_S y^{-|S|} * \sum_T [T \cap E == S]\\
&令g(S) = \sum_T[T \cap E == S].考虑组合意义,即与E的边集恰好为S.考虑容斥,设f(S)为强制与E的边集为S,且使图生成树的方案.那么\\
&f(S) = \sum_{S \subseteq T} g(T)\\
&g(S) = \sum_{S \subseteq T} f(T) * (-1)^{|T| - |S|}\\
ans &= y^n *\sum_{S}y^{-|S|}\sum_{S \subseteq T}f(T) * (-1)^{|T| - |S|}\\
&考虑交换求和顺序,用二项式定理\\
&= y^n * \sum_T f(T) * (y^{-1}-1)^{|T|}\\
&考虑f(T)即将n - T个连通块生成树的方案数.所以\\
&= y^n * \frac{n^n}{n^2} * \sum_{T} * (\prod_{i = 1}^{n-|T|} * s_i) * (\frac{y^{-1}-1}{n})^{|T|}\\
&= \frac{(1-y)^n}{n^2}*\sum_{T \subseteq E}(\prod_{i = 1}^{n-|T|}(s_i*n*\frac{n}{y^{-1}-1}))\\
&令K = s_i*n*\frac{n}{y^{-1}-1}.考虑组合意义.意即将树划分为若干个联通块.\\
&每个连通块有可以选一个点乘以K.所以设dp_{u,0|1}表示当前dp到u的子树,该连通块选了|没选了一个点乘K的贡献.转移分这条边割与不割转移即可\\
&时间复杂度O(n)
\end{aligned}\]
- \(Sub2:\)
- 这一部分是比较显然的.把上面的式子多增加一个枚举E,然后演变乘算一个森林若干个贡献乘积.设出树的生成函数,对其做\(exp\)即可得到森林的生成函数的答案.
\(Tag:组合数学,NTT,生成函数,图计数\)
- 启示:推式子的时候不要一口气推的太跳步,容易推不出来,在草稿纸上一步步推,一步步观察.
- 代码如下
/*WC2019数树*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int read(){
char c = getchar();
int x = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar();
return x;
}
const int N = 4e5 + 10;
typedef pair<int,int> pii;
#define mod 998244353
int qpow(int x,int y){
if(y < 0) return 1;
int ans = 1;
while(y){
if(y & 1) ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod;
y >>= 1;
}
return ans;
}
int n,y,op;
void add(int &x,int y){
x += y - mod;
x += (x >> 31) & mod;
}
namespace Sub0{
pii e[N];
map<pii,bool>h;
#define mp make_pair
void solve(){
int ans = n;
for(int i = 1; i < n; ++i){
int u = read(),v = read();
h[mp(u,v)] = h[mp(v,u)] = 1;
}
for(int i = 1; i < n; ++i){
int u = read(),v = read();
if(h[mp(u,v)]){
ans--;
}
}
ans = qpow(y,ans);
printf("%d\n",ans);
}
}
int G = 3,Gi;
namespace Poly{
int rk[N],g[N],A[N],B[N];
void getrk(int lim,int l){
for(int i = 1; i < lim; ++i) rk[i] = (rk[i>>1] >> 1) | ((i & 1) << (l - 1));
}
void ntt(int *f,int lim,int type){
int n = 1,l = 0;
while(n < lim) n <<= 1,l++;
getrk(n,l);
for(int i = 0; i < lim; ++i) g[i] = f[rk[i]];
for(int i = 0; i < lim; ++i) f[i] = g[i];
for(int j = 1; j < lim; j <<= 1){
int Wn = qpow(type == 1?G:Gi,(mod - 1) / (j << 1));
for(int i = 0,len = j << 1; i < lim; i += len){
int w = 1;
for(int k = 0; k < j; ++k,w = 1ll * w * Wn % mod){
int x = f[i+k],y = 1ll * w * f[i+k+j] % mod;
f[i+k] = (x + y) % mod;
f[i+k+j] = (x - y + mod) % mod;
}
}
}
if(type != 1){
int inv = qpow(lim,mod-2);
for(int i = 0; i < lim; ++i) f[i] = 1ll * f[i] * inv % mod;
}
}
void exp(int *f,int *g,int l,int r){
int n = r - l;
if(n == 1){
if(l != 0) f[l] = 1ll * f[l] * qpow(l,mod-2) % mod;
return;
}
int mid = (l + r) >> 1;
exp(f,g,l,mid);
for(int i = l; i < mid; ++i) A[i-l] = f[i];
for(int i = mid; i < r; ++i) A[i-l] = 0;
for(int i = 0; i < n; ++i) B[i] = 1ll * g[i] * i % mod;
ntt(A,n,1);ntt(B,n,1);
for(int i = 0; i < n; ++i) A[i] = 1ll * A[i] * B[i] % mod;
ntt(A,n,-1);
for(int i = mid; i < r; ++i) add(f[i],A[i-l]);
exp(f,g,mid,r);
}
}
using Poly::getrk;
using Poly::ntt;
using Poly::exp;
int fac[N<<1],invfac[N<<1];
int C(int x,int y){
return 1ll * fac[x] * invfac[y] % mod * invfac[x-y] % mod;
}
namespace Sub1{
int dp[N][2],K;
struct Edge{
int nxt,point;
}edge[N<<1];
int head[N],tot;
void add_edge(int u,int v){
edge[++tot].nxt = head[u];
edge[tot].point = v;
head[u] = tot;
}
void treedp(int u,int fa){
dp[u][0] = 1;dp[u][1] = K;
for(int i = head[u]; i ;i = edge[i].nxt){
int v = edge[i].point;
if(v ^ fa){
treedp(v,u);
int f0 = (1ll * dp[u][0] * dp[v][0] + 1ll * dp[u][0] * dp[v][1]) % mod;
int f1 = (1ll * dp[u][0] * dp[v][1] + 1ll * dp[u][1] * dp[v][0]) % mod;
add(f1,1ll * dp[u][1] * dp[v][1] % mod);
dp[u][0] = f0;
dp[u][1] = f1;
}
}
}
void solve(){
int v = (1 - y + mod) % mod;
v = qpow(v,n);
int inv = 1ll * n * n % mod;
inv = qpow(inv,mod-2);
v = 1ll * v * inv % mod;
K = 1ll * qpow((qpow(y,mod-2) - 1),mod-2) * n % mod;
for(int i = 1; i < n; ++i){
int u = read(),v = read();
add_edge(u,v);add_edge(v,u);
}
treedp(1,0);
int ans = 1ll * v * dp[1][1] % mod;
printf("%d\n",ans);
}
}
namespace Sub2{
int f[N],g[N];
void solve(){
int lim = 1;
Gi = qpow(G,mod-2);
while(lim <= n) lim <<= 1;
fac[0] = 1;
for(int i = 1; i < lim; ++i) fac[i] = 1ll * fac[i-1] * i % mod;
for(int i = 0; i < lim; ++i) invfac[i] = qpow(fac[i],mod-2);
int sn = 1ll * n * n % mod;
int inv = qpow(qpow(y,mod-2) - 1,mod - 2);
for(int i = 1; i < lim; ++i)
f[i] = 1ll * qpow(i,i) * sn % mod * inv % mod * invfac[i] % mod;
g[0] = 1;
exp(g,f,0,lim);
int ans = 1ll * g[n] * fac[n] % mod * qpow((1 - y + mod),n) % mod;
int invn = qpow(qpow(n,4),mod-2);
ans = 1ll * ans * invn % mod;
printf("%d\n",ans);
}
}
int main(){
n = read(),y = read(),op = read();
if(y == 1){
if(op == 0){
puts("1");
}
else if(op == 1){
printf("%d\n",qpow(n,n-2));
}
else{
printf("%lld\n",1ll * qpow(n,n-2)*qpow(n,n-2)%mod);
}
return 0;
}
if(op == 0){
Sub0::solve();
return 0;
}
if(op == 1){
Sub1::solve();
return 0;
}
if(op == 2){
Sub2::solve();
}
return 0;
}