• 题解 AT2064 【[AGC005F] Many Easy Problems】


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

    [largeegin{aligned} f_i&=sum_{x=1}^nleft(inom{n}{i}-sum_{exists e(x,y)} inom{size_y}{i} ight) \ &=ninom{n}{i}-sum_{x=1}^nsum_{exists e(x,y)} inom{size_y}{i} \ end{aligned} ]

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

    [largeegin{aligned} f_i&=ninom{n}{i}-sum_{j=i}^n cnt_jinom{j}{i} \ &=ninom{n}{i}-sum_{j=i}^n cnt_jfrac{j!}{i!(j-i)!} \ &=ninom{n}{i}-frac{1}{i!}sum_{j=i}^n frac{cnt_jj!}{(j-i)!} \ &=ninom{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*C(n,i)%P-ifac[i]*f[i]%P+P)%P);
        return 0;
    }
    
  • 相关阅读:
    用Java随机生成四则运算
    初涉猎软件工程的一些疑问
    201671010124 2016-2017-2 《Java程序设计》
    201671010124 2016-2017-2 《Java程序设计》2
    201671010124 2016-2017-2 《Java程序设计》初见Java
    《贪吃蛇》局部刷新法(C语言,字符界面)
    简单的《贪吃蛇》全局刷新法(C语言,字符界面)
    读取本地的json文件
    js获取当前页面的URL并且截取?之后的数据,返回json
    全面实用的Tab切换
  • 原文地址:https://www.cnblogs.com/lhm-/p/13697492.html
Copyright © 2020-2023  润新知