Atcoder 005 Editorial F - Many Easy Problems

前言

省选GG之后成为文化课选手。。
文化课都补不完的说
然后全部训练时间,都拿去补文化课了。。
于是今天中午终于做了一道题。。

题意

给你一棵树
然后对于所有的k
答案是你在n里面选择k个点,他的代价就是最少要选多少个点,他可以包含这个联通块
Ckn C n k 种方案的代价和

题解

这题看起来不是很好做。。
然后我们考虑每一个点对答案的贡献
这个也不好做
那么我们就想能不能减去不合法的
那么对于一个点,他对 k k 的贡献就是
Cnk
Ckn C n k 提出来,就是要求有后面这个的总和
有什么是不合法的呢?
就是全的点,都在删除他时候,剩余树的某一个联通块里面
这个的话,就是 Ckai ∑ C a i k
考虑展开这个东西
Ckai=(ai)!k!(aik)! C a i k = ( a i ) ! k ! ( a i − k ) !
然后 k! k ! 可以提出来,那么剩下的就是一个卷积的形式来做了
然后我们由于要把所有点一起算,所以上面要乘上一个系数 p[ai] p [ a i ]
表示联通块大小为 ai a i 的有多少个
然后这题就做完了
CODE:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const LL MOD=924844033;
const LL g=5;
const LL gi=554906420;
const LL N=200005*8;
struct qq
{
    LL x,y,last;
}e[N*2];LL num,last[N];
LL n;
LL pow (LL x,LL y)
{
    if (y==1) return  x;
    LL lalal=pow(x,y>>1);
    lalal=lalal*lalal%MOD;
    if (y&1) lalal=lalal*x%MOD;
    return lalal;
}
void init (LL x,LL y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
LL tot[N];
LL a[N],b[N];
void dfs (LL x,LL fa)
{
    tot[x]=1;
    for (LL u=last[x];u!=-1;u=e[u].last)
    {
        LL y=e[u].y;
        if (y==fa) continue;
        dfs(y,x);
        tot[x]+=tot[y];
    }
    if (fa!=0)  {a[tot[x]]++;a[n-tot[x]]++;}
}
LL JC[N],inv[N];
LL bin[N];
void ntt (LL *a,LL n,LL o)
{
    for (LL u=0;u<n;u++)    bin[u]=(bin[u>>1]>>1)|((u&1)*(n>>1));
    for (LL u=0;u<n;u++)
        if (u<bin[u])
            swap(a[u],a[bin[u]]);
    for (LL u=1;u<n;u<<=1)
    {
        LL w,t,wn=pow(o==1?g:gi,(MOD-1)/(u<<1));
        for (LL i=0;i<n;i=i+(u<<1))
        {
            w=1;
            for (LL j=0;j<u;j++)
            {
                t=w*a[u+i+j]%MOD;
                a[u+i+j]=(a[i+j]-t+MOD)%MOD;
                a[i+j]=(a[i+j]+t)%MOD;
                w=w*wn%MOD;
            }
        }
    }
    if (o==-1)
    {
        LL Inv=pow(n,MOD-2);for (LL u=0;u<n;u++) a[u]=a[u]*Inv%MOD;
    }
}
int main()
{
    //printf("%lld\n",pow(g,MOD-2));
    num=0;memset(last,-1,sizeof(last));
    scanf("%lld",&n);
    for (LL u=1;u<n;u++)
    {
        LL x,y;
        scanf("%lld%lld",&x,&y);
        init(x,y);init(y,x);
    }
    dfs(1,0);
    JC[0]=1;for (LL u=1;u<=n;u++) JC[u]=JC[u-1]*u%MOD;
    inv[n]=pow(JC[n],MOD-2);for (LL u=n-1;u>=0;u--) inv[u]=inv[u+1]*(u+1)%MOD;
    for (LL u=1;u<=n;u++) a[u]=a[u]*JC[u]%MOD;
    for (LL u=0;u<=n;u++) b[u]=inv[n-u];
    LL now=1;while (now<=n) now<<=1;now<<=1;
    ntt(a,now,1);ntt(b,now,1);
    for (LL u=0;u<now;u++) a[u]=a[u]*b[u]%MOD;
    ntt(a,now,-1);
    for (LL u=1;u<=n;u++)
    {
        now=JC[n]*inv[u]%MOD*inv[n-u]%MOD*n%MOD;
        now=(now-a[n+u]*inv[u]%MOD+MOD)%MOD;
        printf("%lld\n",now);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值