AtCoder Grand Contest 005 F - Many Easy Problems

题意:

f(k) f ( k ) 表示在树上任选k个点所组成的最小联通块大小的和。
求出所有 f(k) f ( k )

题解:

这种题一般都单独考虑每个点的贡献。
单点贡献显然就是 Cknyson[x]Cksize[y] C n k − ∑ y ∈ s o n [ x ] C s i z e [ y ] k
所以 ans[k]=nx=1Cknyson[x]Cksize[y] a n s [ k ] = ∑ x = 1 n C n k − ∑ y ∈ s o n [ x ] C s i z e [ y ] k
观察发现无论 k k 取多少,cik的系数都是不变的,设为 a[i] a [ i ]
ans[k]=nia[i]Cki a n s [ k ] = ∑ i n a [ i ] ∗ C i k
这个式子就可以卷积了。
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const LL p=924844033,yg=5;
LL n;
struct node{
    LL y,next;
}a[400010];LL len=0,last[200010];
LL f[200010],inv[200010],A[800010],B[800010],bin[800010];
void ins(LL x,LL y)
{
    a[++len].y=y;
    a[len].next=last[x];last[x]=len;
}
void pre()
{
    f[0]=f[1]=inv[0]=inv[1]=1;
    for(LL i=2;i<=200000;i++) f[i]=f[i-1]*i%p,inv[i]=(p-p/i)*inv[p%i]%p;
    for(LL i=2;i<=200000;i++) inv[i]=inv[i-1]*inv[i]%p;
}
LL pow(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1) ans=ans*a%p;
        a=a*a%p;b>>=1;
    }
    return ans;
}
LL size[200010];
void ntt(LL *a,LL n,LL op)
{
    for(LL i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
    for(LL i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
    for(LL i=1;i<n;i<<=1)
    {
        LL wn=pow(yg,op==1?(p-1)/(2*i):(p-1)-(p-1)/(2*i)),w,t;
        for(LL j=0;j<n;j+=i<<1)
        {
            w=1;
            for(LL k=0;k<i;k++)
            {
                t=a[i+j+k]*w%p;w=w*wn%p;
                a[i+j+k]=(a[j+k]-t+p)%p;a[j+k]=(a[j+k]+t)%p;
            }
        }
    }
    if(op==-1)
    {
        LL Inv=pow(n,p-2);
        for(LL i=0;i<n;i++) a[i]=a[i]*Inv%p;
    }
}
void dfs(LL x,LL fa)
{
    size[x]=1;A[n]++;
    for(LL i=last[x];i;i=a[i].next)
    {
        LL y=a[i].y;
        if(y==fa) continue;
        dfs(y,x);size[x]+=size[y];
        A[size[y]]--;
    }
    if(n-size[x]) A[n-size[x]]--;
}
int main()
{
    scanf("%lld",&n);
    for(LL i=1;i<n;i++)
    {
        LL x,y;scanf("%lld %lld",&x,&y);
        ins(x,y);ins(y,x);
    }
    pre();dfs(1,0);
    for(LL i=0;i<=n;i++) B[i]=inv[i],A[i]+=A[i]<0?p:0,A[i]=A[i]*f[i]%p;
    reverse(B,B+n+1);
    LL N=1;while(N<(n+1)<<1) N<<=1;
    ntt(A,N,1);ntt(B,N,1);
    for(LL i=0;i<N;i++) A[i]=A[i]*B[i]%p;
    ntt(A,N,-1);
    for(LL i=1;i<=n;i++) printf("%lld\n",inv[i]*A[n+i]%p);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值