UOJ NOI Round #5 d1t2 40pts
tag:树形dp,指数形生成函数,同构
只会45,noi结束再看剩下部分吧w
首先对于同构问题,自然想到先找重心再 dp。
如果一个染色方案中两个子树本质相同,那么在不考虑颜色的情况下,这两个子树也本质相同,而不考虑颜色的话就是一个背包 dp。
但是这道题要考虑颜色的分配,所以可以考虑使用 egf 去 dp。
对于 \(k\) 个同构的子树,假设它们的 egf 为 \(f(v)\)(既然同构,那么 egf 一定相同)
那么它们的贡献就是
\[\sum_{i=0}^k\frac{(f(v)-1)^i}{i!}
\]
去掉 \(1\) 是因为这里枚举的 \(i\) 代表的是『染了色』的子树个数,而常数项的那个 \(1\) 代表的是不染色。
然后当前点 \(cur\) 的 egf 就是用 \(x+1\) 依次乘上儿子的贡献。
直接暴力多项式运算,复杂度和树形背包的复杂度一样为 \(O(n^2)\)
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch; bool flag=false;
while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
enum{
MAXN = 3005,
MOD = 998244353
};
inline int ksm(int base, int k=MOD-2){
int res=1;
while(k){
if(k&1)
res = 1ll*res*base%MOD;
base = 1ll*base*base%MOD;
k >>= 1;
}
return res;
}
inline void upd(int &a, long long b){a = (a+b)%MOD;}
int pw[MAXN], hs[MAXN];
int n;
vector<int>to[MAXN];
inline void Add_Edge(int f, int t){
to[f].push_back(t);
to[t].push_back(f);
}
inline bool cmp(const int &u, const int &v){return hs[u]<hs[v];}
int rt, w[MAXN], sz[MAXN];
void dfs(int x, int y){
sz[x] = 1;
for(int v:to[x]) if(v!=y){
dfs(v,x);
sz[x] += sz[v];
w[x] = max(w[x],sz[v]);
}
w[x] = max(w[x],n-sz[x]);
w[0] = min(w[0],w[x]);
}
int f[MAXN][MAXN], tmp[MAXN], g[MAXN], invjc[MAXN], jc[MAXN], tmpg[MAXN];
inline void mul(int x, int v, int k){
memset(g,0,sizeof g);
memset(tmp,0,sizeof tmp);
g[0] = 1; tmp[0] = 1;
int tp=0;
for(int i=1; i<=k; i++){
for(int j=0; j<=tp; j++) for(int p=1; p<=sz[v]; p++) upd(tmpg[j+p],1ll*g[j]*f[v][p]);
tp += sz[v]; for(int j=0; j<=tp; j++) g[j] = tmpg[j], tmpg[j] = 0, upd(tmp[j],1ll*invjc[i]*g[j]);
}
for(int i=0; i<=sz[x]; i++) for(int j=0; j<=tp; j++) upd(tmpg[i+j],1ll*f[x][i]*tmp[j]);
sz[x] += tp; for(int i=0; i<=sz[x]; i++) f[x][i] = tmpg[i], tmpg[i] = 0;
}
void dp(int x, int y){
sz[x] = 1;
f[x][0] = 1; f[x][1] = (x<=n);
for(int i=0, tp=to[x].size(); i<tp; i++) if(to[x][i]==y){swap(to[x][i],to[x][tp-1]);to[x].pop_back();break;}
for(int v:to[x]) dp(v,x);
sort(to[x].begin(),to[x].end(),cmp);
int kkk=0;
for(int i=0, tp=to[x].size(), cnt=1; i<tp; i++)
if(i==tp-1 or hs[to[x][i]]!=hs[to[x][i+1]]) mul(x,to[x][i],cnt), cnt = 1;
else cnt++;
hs[x] = sz[x];
for(int v:to[x]) hs[x] = (1ll*hs[x]*pw[sz[v]]+hs[v])%MOD;
}
inline void link(int x, int y){
for(int i=0, tp=to[x].size(); i<tp; i++) if(to[x][i]==y){swap(to[x][i],to[x][tp-1]);to[x].pop_back();break;}
for(int i=0, tp=to[y].size(); i<tp; i++) if(to[y][i]==x){swap(to[y][i],to[y][tp-1]);to[y].pop_back();break;}
to[n+1].push_back(x); to[n+1].push_back(y);
}
int main(){
// freopen("ex_color3.in","r",stdin);
Read(n);
pw[0] = 1; pw[1] = 20041107; for(int i=2; i<=n; i++) pw[i] = 1ll*pw[i-1]*pw[1]%MOD;
jc[0] = 1; for(int i=1; i<=n; i++) jc[i] = 1ll*i*jc[i-1]%MOD;
invjc[n] = ksm(jc[n]); for(int i=n; i; i--) invjc[i-1] = 1ll*invjc[i]*i%MOD;
for(int i=1; i<n; i++){
int f, t;
Read(f); Read(t);
Add_Edge(f,t);
}
w[0] = n;
dfs(1,0);
for(int i=1; i<=n; i++) if(w[i]==w[0])
if(rt) link(rt,i), rt = n+1;
else rt = i;
dp(rt,0);
// printf("%d\n",sz[rt]);
for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) if(hs[i]==hs[j]) assert(sz[i]==sz[j]);
for(int i=1; i<=n; i++) printf("%lld ",1ll*jc[i]*f[rt][i]%MOD); puts("");
return 0;
}