[题解] Atcoder AGC 005 F Many Easy Problems NTT,组合数学
阅读原文时间:2023年07月08日阅读:4

题目

观察当k固定时答案是什么。先假设每个节点对答案的贡献都是\(\binom{n}{k}\),然后再减掉某个点没有贡献的选点方案数。对于一个节点i,它没有贡献的方案数显然就是所有k个节点都选在i连出去的某一个子树内的方案数。枚举节点i,把i连出去的每一个子树的size都加入一个序列c,则答案为\(\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\binom{c_i}{k}\)。

考虑\(k=1\cdots n\)的情况:

\(ans_k=\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\binom{c_i}{k}=\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\binom{c_i}{c_i-k}=\binom{n}{k}\cdot n-\sum_{i=0}^{|c|-1}\frac{c_i!}{k!(c_i-k)!}\)

\(-ans_k k!+\binom nk\cdot n\cdot k!=\sum_{i=0}^{|c|-1}\frac{c_i!}{(c_i-k)!}\)

统计每种\(c_i\)的出现次数后,右边就变成了一个减法卷积的形式,一遍NTT即可。需要注意题目中的模数的原根是5.时间复杂度\(O(nlogn)\)。

点击查看代码

#include <bits/stdc++.h>

#define rep(i,n) for(int i=0;i<n;++i)
#define repn(i,n) for(int i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

const LL MOD=924844033;

LL qpow(LL x,LL a)
{
    LL res=x,ret=1;
    while(a>0)
    {
        if((a&1)==1) ret=ret*res%MOD;
        a>>=1;
        res=res*res%MOD;
    }
    return ret;
}

namespace poly
{
  vector <LL> rev;
  void ntt(vector <LL> &a,LL G)
  {
    LL nn=a.size(),gn,g,x,y;vector <LL> tmp=a;
    rep(i,nn) a[i]=tmp[rev[i]];
    for(int len=1;len<nn;len<<=1)
    {
      gn=qpow(G,(MOD-1)/(len<<1));
      for(int i=0;i<nn;i+=(len<<1))
      {
        g=1;
        for(int j=i;j<i+len;++j,(g*=gn)%=MOD)
        {
          x=a[j];y=a[j+len]*g%MOD;
          a[j]=(x+y)%MOD;a[j+len]=(x-y+MOD)%MOD;
        }
      }
    }
  }
  vector <LL> convolution(vector <LL> a,vector <LL> b,LL G)
  {
    LL nn=1,bt=0,sv=a.size()+b.size()-1;while(nn<a.size()+b.size()-1) nn<<=1LL,++bt;
    while(a.size()<nn) a.pb(0);while(b.size()<nn) b.pb(0);
    rev.clear();
    rep(i,nn)
    {
      rev.pb(0);
      rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
    }
    ntt(a,G);ntt(b,G);
    rep(i,nn) (a[i]*=b[i])%=MOD;
    ntt(a,qpow(G,MOD-2));
    while(a.size()>sv) a.pop_back();
    LL inv=qpow(nn,MOD-2);
    rep(i,a.size()) (a[i]*=inv)%=MOD;
    return a;
  }
  vector <LL> inverse(vector <LL> a,LL G)
  {
    if(a.size()==1) return vector <LL>{qpow(a[0],MOD-2)};
    vector <LL> aa=a;while(aa.size()>(a.size()+1)>>1) aa.pop_back();
    vector <LL> bb=inverse(aa,G);
    LL nn=1,bt=0,sv=a.size();while(nn<a.size()*2) nn<<=1LL,++bt;
    while(a.size()<nn) a.pb(0);while(bb.size()<nn) bb.pb(0);
    rev.clear();
    rep(i,nn)
    {
      rev.pb(0);
      rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
    }
    ntt(a,G);ntt(bb,G);
    rep(i,nn) a[i]=(2LL-a[i]*bb[i]%MOD+MOD)*bb[i]%MOD;
    ntt(a,qpow(G,MOD-2));
    while(a.size()>sv) a.pop_back();
    LL inv=qpow(nn,MOD-2);
    rep(i,a.size()) (a[i]*=inv)%=MOD;
    return a;
  }
  vector <LL> sqrt1(vector <LL> a,LL G)//常数项为1
  {
    if(a.size()==1) return vector <LL>{1};
    vector <LL> aa=a;while(aa.size()>(a.size()+1)>>1) aa.pop_back();
    vector <LL> bb=sqrt1(aa,G);while(bb.size()<a.size()) bb.pb(0);
    vector <LL> bbb=inverse(bb,G);
    LL nn=1,bt=0,sv=a.size();while(nn<a.size()*2) nn<<=1LL,++bt;
    while(a.size()<nn) a.pb(0);while(bb.size()<nn) bb.pb(0);while(bbb.size()<nn) bbb.pb(0);
    rev.clear();
    rep(i,nn)
    {
      rev.pb(0);
      rev[i]=(rev[i>>1]>>1)|((i&1)<<(bt-1));
    }
    LL mul=qpow(2,MOD-2);
    ntt(a,G);ntt(bb,G);ntt(bbb,G);
    rep(i,nn) a[i]=mul*(bb[i]+bbb[i]*a[i]%MOD)%MOD;
    ntt(a,qpow(G,MOD-2));
    while(a.size()>sv) a.pop_back();
    LL inv=qpow(nn,MOD-2);
    rep(i,a.size()) (a[i]*=inv)%=MOD;
    return a;
  }
}

LL n,sz[200010],fac[200010],inv[200010],ans[200010];
vector <LL> g[200010],A,B,C;

LL CC(LL nn,LL mm){return fac[nn]*inv[mm]%MOD*inv[nn-mm]%MOD;}

void dfs(LL pos,LL par)
{
  sz[pos]=1;
  rep(i,g[pos].size()) if(g[pos][i]!=par)
  {
    dfs(g[pos][i],pos);
    sz[pos]+=sz[g[pos][i]];
    ++A[sz[g[pos][i]]];
  }
  if(pos!=1) ++A[n-sz[pos]];
}

int main()
{
  fac[0]=1;repn(i,200005) fac[i]=fac[i-1]*(LL)i%MOD;
  rep(i,200003) inv[i]=qpow(fac[i],MOD-2);
  cin>>n;
  LL x,y;
  rep(i,n-1)
  {
    scanf("%lld%lld",&x,&y);
    g[x].pb(y);g[y].pb(x);
  }
  rep(i,n) A.pb(0);
  dfs(1,0);
  rep(i,n) (A[i]*=fac[i])%=MOD;
  rep(i,n) B.pb(inv[i]);
  reverse(B.begin(),B.end());
  C=poly::convolution(A,B,5);
  repn(i,n)
  {
    ans[i]=CC(n,i)*n%MOD;
    (ans[i]+=MOD-(i+n-1>=C.size() ? 0LL:C[i+n-1])*inv[i]%MOD)%=MOD;
  }
  repn(i,n) printf("%lld\n",ans[i]);
    return 0;
}