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;
}
posted @ 2021-07-20 14:40  oisdoaiu  阅读(40)  评论(0编辑  收藏  举报