题解 AT2064 【[AGC005F] Many Easy Problems】

考虑每个点 \(x\) 对答案的贡献,即总方案数减去不合法方案数,得:

\[\large\begin{aligned} f_i&=\sum_{x=1}^n\left(\binom{n}{i}-\sum_{\exists e(x,y)} \binom{size_y}{i}\right) \\ &=n\binom{n}{i}-\sum_{x=1}^n\sum_{\exists e(x,y)} \binom{size_y}{i} \\ \end{aligned} \]

\(cnt_i\) 为子树大小为 \(i\) 的子树个数,得:

\[\large\begin{aligned} f_i&=n\binom{n}{i}-\sum_{j=i}^n cnt_j\binom{j}{i} \\ &=n\binom{n}{i}-\sum_{j=i}^n cnt_j\frac{j!}{i!(j-i)!} \\ &=n\binom{n}{i}-\frac{1}{i!}\sum_{j=i}^n \frac{cnt_jj!}{(j-i)!} \\ &=n\binom{n}{i}-\frac{1}{i!}\sum_{j=0}^{n-i} \frac{cnt_{i+j}(i+j)!}{j!} \\ \end{aligned} \]

发现第二项是一个减法卷积的形式,用 \(NTT\) 即可求解。

#include<bits/stdc++.h>
#define maxn 800010
#define P 924844033
#define G 5
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
ll n;
ll rev[maxn],f[maxn],g[maxn],fac[maxn],ifac[maxn],siz[maxn],cnt[maxn];
struct edge
{
    int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
    e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
ll qp(ll x,ll y)
{
    ll v=1;
    while(y)
    {
        if(y&1) v=v*x%P;
        x=x*x%P,y>>=1;
    }
    return v;
}
int calc(int n)
{
    int lim=1;
    while(lim<=n) lim<<=1;
    for(int i=0;i<lim;++i)
        rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
    return lim;
}
void NTT(ll *a,int lim,int type)
{
    for(int i=0;i<lim;++i)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    for(int len=1;len<lim;len<<=1)
    {
        ll wn=qp(G,(P-1)/(len<<1));
        for(int i=0;i<lim;i+=len<<1)
        {
            ll w=1;
            for(int j=i;j<i+len;++j,w=w*wn%P)
            {
                ll x=a[j],y=w*a[j+len]%P;
                a[j]=(x+y)%P,a[j+len]=(x-y+P)%P;
            }
        }
    }
    if(type==1) return;
    ll inv=qp(lim,P-2);
    for(int i=0;i<lim;++i) a[i]=a[i]*inv%P;
    reverse(a+1,a+lim);
}
void mul(ll *f,ll *g)
{
    int lim=calc(n<<1);
    NTT(f,lim,1),NTT(g,lim,1);
    for(int i=0;i<lim;++i) f[i]=f[i]*g[i]%P;
    NTT(f,lim,-1);
}
void dfs(int x,int fa)
{
    siz[x]=1;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x),siz[x]+=siz[y];
    }
    cnt[siz[x]]++,cnt[n-siz[x]]++;
}
ll C(ll n,ll m)
{
    return fac[n]*ifac[m]%P*ifac[n-m]%P;
}
void init()
{
    fac[0]=ifac[0]=1;
    for(int i=1;i<=n;++i) fac[i]=fac[i-1]*i%P;
    ifac[n]=qp(fac[n],P-2);
    for(int i=n-1;i;--i) ifac[i]=ifac[i+1]*(i+1)%P;
}
int main()
{
    read(n),init();
    for(int i=1;i<n;++i)
    {
        int x,y;
        read(x),read(y);
        add(x,y),add(y,x);
    }
    dfs(1,0),g[0]=1;
    for(int i=1;i<=n;++i) f[i]=cnt[n-i]*fac[n-i]%P,g[i]=ifac[i];
    mul(f,g),reverse(f,f+n+1);
    for(int i=1;i<=n;++i) printf("%lld\n",(n*C(n,i)%P-ifac[i]*f[i]%P+P)%P);
    return 0;
}
posted @ 2020-09-19 20:34  lhm_liu  阅读(246)  评论(0编辑  收藏  举报