AT2064 [AGC005F] Many Easy Problems 容斥+NTT

题意:令 $f(i)$ 表示对于 $inom{n}{i}$ 种包含 $i$ 个点的最小连通块的总节点个数和.

面对个数和/价值和且点和点之间没有限制条件的问题时可以考虑单独处理每个点的贡献.    

考虑点 $j$ 对 $f(i)$ 的贡献.  

那么 $j$ 对 $f(i)$ 有贡献时分两种情况.   

1. $j$ 是 $i$ 个点中的一个.

2. $j$ 不是 $i$ 个点中的一个,但是 $j$ 被这个连通块所包含,那么就意味着至少 $j$ 的两个儿子中有 $i$ 集合中的点.      

对于情况 1,方案数为 $inom{n-1}{i-1}.$  

对于情况 2,方案数为 $inom{n-1}{i}-sum_{v subseteq son[j] } inom{son[v]}{i}.$    

两种情况总和为:$inom{n}{i}-sum_{v subseteq son[j] } inom{son[v]}{i}$ 

那么对于所有点加和,得 $f(i)=n inom{n}{i}- sum_{j=i}^{n} inom{j}{i} num(j).$   

$Rightarrow f(i)=n inom{n}{i} - i!sum_{j=i}^{n} frac{j!num(j)}{(j-i)!}.$  

前面的是常数,但后后面的式子差是定值,所以将系数翻转做一遍 NTT 即可.    

#include <cstdio>  
#include <cstring> 
#include <cmath> 
#include <vector>  
#include <algorithm>      
#define ll long long   
#define N 1000003 
#define MAX 200005       
#define mod 924844033
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
int n,edges; 
int hd[N],to[N],nex[N],bu[N],fac[N],inv[N],t[N],g[N],size[N],Ans[N];   
void add(int u,int v) 
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;    
}
int qpow(int x,int y) 
{ 
    int tmp=1; 
    for(;y;y>>=1,x=(ll)x*x%mod) 
        if(y&1) tmp=(ll)tmp*x%mod; 
    return tmp;   
}
int INV(int x) { return qpow(x,mod-2); }      
void NTT(int *a,int len,int flag) 
{
    int i,j,k,mid;     
    for(i=k=0;i<len;++i) 
    {
        if(i>k) swap(a[i],a[k]); 
        for(j=len>>1;(k^=j)<j;j>>=1);    
    }
    for(mid=1;mid<len;mid<<=1) 
    {
        int wn=qpow(5,(mod-1)/(mid<<1));     
        if(flag==-1) wn=INV(wn);      
        for(i=0;i<len;i+=mid<<1) 
        { 
            int w=1;      
            for(j=0;j<mid;++j) 
            {
                int x=a[i+j],y=(ll)w*a[i+j+mid]%mod;         
                a[i+j]=(ll)(x+y)%mod;    
                a[i+j+mid]=(ll)(x-y+mod)%mod;    
                w=(ll)w*wn%mod;    
            }
        }
    }
    if(flag==-1) 
    {
        int in=INV(len); 
        for(i=0;i<len;++i) a[i]=(ll)a[i]*in%mod;    
    }
} 
int C(int x,int y) 
{
    if(x<y||x<0||y<0) return 0;   
    return (ll)fac[x]*inv[y]%mod*inv[x-y]%mod;    
}
void init() 
{
    int i,j;     
    fac[0]=inv[0]=1;     
    for(i=1;i<MAX;++i) fac[i]=(ll)fac[i-1]*i%mod,inv[i]=INV(fac[i]);       
}
void dfs(int u,int ff) 
{
    size[u]=1; 
    for(int i=hd[u];i;i=nex[i]) 
    {
        int v=to[i]; 
        if(v==ff) continue;    
        dfs(v,u); 
        size[u]+=size[v];    
        bu[size[v]]++;   
    }  
    bu[n-size[u]]++;     
}
int main() 
{     
    // setIO("input");   
    int i,j;    
    init();           
    scanf("%d",&n);   
    for(i=1;i<n;++i) 
    {
        int x,y;  
        scanf("%d%d",&x,&y),add(x,y),add(y,x);  
    }      
    dfs(1,0);  
    int ans=0;         
    for(i=0;i<=n;++i) g[i]=(ll)bu[i]*fac[i]%mod,t[n-i]=inv[i];          
    int lim=1;   
    while(lim<=2*n) lim<<=1;   
    NTT(g,lim,1),NTT(t,lim,1); 
    for(i=0;i<lim;++i) g[i]=(ll)g[i]*t[i]%mod;   
    NTT(g,lim,-1);      
    for(i=0;i<=n;++i)  Ans[i]=(ll)g[i+n]*inv[i]%mod;       
    for(i=1;i<=n;++i) 
    {
        int tmp=(ll)n*C(n,i)%mod;      
        printf("%d
",(ll)(tmp-Ans[i]+mod)%mod);   
    }        
    return 0;   
}

  

原文地址:https://www.cnblogs.com/guangheli/p/12302623.html