AGC005F Many Easy Problems

题目

你有一棵无根树(n个结点,1~n标号),和一个整数k。
对于一个树上的结点集合S,可以用一个子树把S中的点全部包含,这里子树指的是原树结点的一个子集,并且这些结点全部连通。对于集合S,令f(S)表示这样的子树最少所包含的结点数目。
共有C(n,k)种方法选出树上的k个结点。对于其中每种方法,设选出的点集为S,你需要求出所有这样的f(S)的总和。
答案可能很大,需要对924844033取模。 你需要对所有k=1~n都回答上述问题。

题解

考虑暴力怎么做。
最直接的想法,就是考虑一个点对 Ans[k] A n s [ k ] 的贡献。
Ans[k]=Σni=1(nk)Σjson[i](siz[j]k)[i1](nsiz[i]k) A n s [ k ] = Σ i = 1 n ( k n ) − Σ j ∈ s o n [ i ] ( k s i z [ j ] ) − [ i ≠ 1 ] ∗ ( k n − s i z [ i ] )
然后 O(n2) O ( n 2 ) 就能拿到40分了。
考虑答案式子。
Ans[k]=nCkng[k] A n s [ k ] = n ∗ C n k − g [ k ]

k!g[k]=Σik cnt[i]i!1(ik)! k ! ∗ g [ k ] = Σ i ≥ k   c n t [ i ] ∗ i ! ∗ 1 ( i − k ) !

其中, cnt[k] c n t [ k ] 表示在原先的式子里 siz[k] s i z [ k ] 出现的次数。
f(i)=cnt[i]i! f ( i ) = c n t [ i ] ∗ i ! h(i)=1i! h ( i ) = 1 i !
k!g[k]=Σikf(i)h(ik) k ! ∗ g [ k ] = Σ i ≥ k f ( i ) ∗ h ( i − k )
好像我看不出什么。
于是我在草稿纸上画了画,将 i i 看成ni
于是整个式子又变成了 k!g[k]=Σikf(i)h(nki) k ! ∗ g [ k ] = Σ i ≥ k f ′ ( i ) ∗ h ( n − k − i )
其中 f(i)=f(ni) f ′ ( i ) = f ( n − i )
然后这就是卷积的形式了。
由于924844033的原根是5,所以就可以直接用 NTT N T T

代码

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define N 200010
#define M 525000 
#define LL long long
#define mo 924844033
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
    int to,next;
};note edge[N*2];
int tot,head[N];
LL i,j,k,l,n,m,ans;
LL u,v,L,len; 
LL jc[N],ny[N],fa[N];
LL siz[N],f[N],h[N];
LL f1[M],f2[M];
LL w[M],_w[M],Rev[M];
LL Ans[N];
LL read(){
    LL fh=1,rs=0;char ch;
    while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
    return fh*rs;
}
void write(LL x){
    if(x>9)write(x/10);
    P(x%10+'0');
}
void lb(int x,int y){
    edge[++tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
LL ksm(LL x,LL y){
    LL rs=1;
    for(;y;y>>=1,x=(x*x)%mo)
    if(y&1)rs=(rs*x)%mo;
    return rs;
}
void pre_(){
    int i;
    jc[0]=jc[1]=ny[0]=ny[1]=1;
    fo(i,2,N-10)jc[i]=(jc[i-1]*i)%mo;
    ny[N-10]=ksm(jc[N-10],mo-2);
    fd(i,N-11,2)ny[i]=(ny[i+1]*(i+1))%mo; 
}
LL C(LL n,LL m){
    if(n<m)return 0;
    return ((jc[n]*ny[m])%mo*ny[n-m])%mo;
}
void add(LL &x,LL y){
    x=(x+y+mo)%mo; 
}
void dg(int x){
    int i;
    siz[x]=1;
    for(i=head[x];i;i=edge[i].next)
        if(fa[x]^edge[i].to){
            fa[edge[i].to]=x;
            dg(edge[i].to);
            f[siz[edge[i].to]]++;
            siz[x]+=siz[edge[i].to];
        }
    if(x^1)f[n-siz[x]]++;
}
void NTT(LL *y,LL *w){
    LL i,h,k;
    fo(i,0,L-1)
    if(i<Rev[i])
    swap(y[i],y[Rev[i]]);
    for(h=2;h<=L;h<<=1){
        for(i=0;i<L;i+=h){
            fo(k,i,i+(h>>1)-1){
                LL u=y[k],t=(w[L/h*(k-i)]*y[k+(h>>1)])%mo;
                y[k]=(u+t)%mo;
                y[k+(h>>1)]=((u-t)%mo+mo)%mo;
            }
        }
    }
}
int main(){
    n=read();
    fo(i,1,n-1){
        u=read();v=read();
        lb(u,v);lb(v,u);
    }
    pre_();
    dg(1);
    fo(i,1,n)f[i]=(f[i]*jc[i])%mo;
    fo(i,0,n)h[i]=ny[i];
    len=0;
    for(L=1;L<(n+1)*2;L<<=1)len++;
    w[0]=w[L]=1;
    w[1]=ksm(5,(mo-1)/L);
    fo(i,1,L-1) w[i]=w[i-1]*1ll*w[1]%mo;
    fo(i,0,L) _w[i]=w[L-i];
    fo(i,0,L-1)Rev[i]=Rev[i>>1]>>1|(i&1)<<len-1;
    fo(i,0,n)f1[i]=f[n-i];
    fo(i,n+1,L-1)f1[i]=0;
    fo(i,0,n)f2[i]=h[i];
    fo(i,n+1,L-1)f2[i]=0;
    NTT(f1,w);
    NTT(f2,w);
    fo(i,0,L-1)f1[i]=f1[i]*f2[i]%mo;
    NTT(f1,_w);
    LL inv=ksm(L,mo-2);
    fo(i,0,L-1)f1[i]=(f1[i]*inv)%mo;
    fo(i,1,n)Ans[i]=(f1[n-i]*ny[i])%mo;
    fo(i,1,n)Ans[i]=(n*C(n,i)%mo-Ans[i]+mo)%mo;
    fo(i,1,n)write(Ans[i]),P('\n');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值