[AGC005F]Many Easy Problems-FFT-容斥原理

Many Easy Problems

Problem Statement

One day, Takahashi was given the following problem from Aoki:

You are given a tree with N vertices and an integer K. The vertices are numbered 1 through N. The edges are represented by pairs of integers (ai,bi).
For a set S of vertices in the tree, let f(S) be the minimum number of the vertices in a subtree of the given tree that contains all vertices in S.
There are ways to choose K vertices from the trees. For each of them, let S be the set of the chosen vertices, and find the sum of f(S) over all ways.
Since the answer may be extremely large, print it modulo 924844033(prime).
Since it was too easy for him, he decided to solve this problem for all K=1,2,…,N.

Constraints

2≦N≦200,000
1≦ai,bi≦N
The given graph is a tree.

Input

The input is given from Standard Input in the following format:

N
a1 b1
a2 b2
:
aN−1 bN−1

Output

Print N lines. The i-th line should contain the answer to the problem where K=i, modulo 924844033.

Sample Input 1

3
1 2
2 3

Sample Output 1

3
7
3

The diagram above illustrates the case where K=2. The chosen vertices are colored pink, and the subtrees with the minimum number of vertices are enclosed by red lines.

Sample Input 2

4
1 2
1 3
1 4

Sample Output 2

4
15
13
4

Sample Input 3

7
1 2
2 3
2 4
4 5
4 6
6 7

Sample Output 3

7
67
150
179
122
45
7

想了很久很久然而只是想到了正解的很小一部分……
看了提示后推式子推了好久却就是差那么一点点没有完全推出来……
感觉自己好⑨啊


思路:
首先得想出个多项式时间内的暴力吧。

貌似直接考虑每个k时总体的答案有些不科学,那就单独考虑每个点的贡献。

可以发现,一个点被加入贡献,当且仅当把这个节点视为根节点后,所有选中的节点不在同一棵子树内。

[————–上面为提示因为本蒟蒻就想到这里—————-]

因为每个点贡献为1,所以可以发现答案为如下式子:

ansk=i=1n((nk)jchi(sizejk))

可以发现这个 sizej 很兹瓷合并同类项啊……
那么令 pi=[sizej==i]
然后继续推新式子:

ansk=1k!(nn!(nk)!i=kn1pii!(ik)!)

pn=n ,原先的 pi=pi ,变成:

ansk=1k!i=knpii!(ik)!
ans=k=1n(1k!i=knpii!(ik)!)

然后可以发现右边是个卷积……
做完了……

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0' || '9'<ch)ch=getchar();
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x;
}

typedef long long ll;
const int N=400009;
const ll md=924844033;

int n,m,l;
int to[N<<1],nxt[N<<1],beg[N],tot;
int rev[N<<1],siz[N];
ll a[N<<1],b[N<<1],fac[N],inv[N];

inline void chk(ll &a){if(a<0)a+=md;}

inline ll qpow(ll a,ll b)
{
    ll ret=1ll;
    while(b)
    {
        if(b&1ll)
            ret=ret*a%md;
        a=a*a%md;
        b>>=1;
    }
    return ret;
}

inline void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=beg[u];
    beg[u]=tot;
}

inline void dfs(int u,int fa)
{
    a[n]++;siz[u]=1;
    for(int i=beg[u];i;i=nxt[i])
        if(to[i]!=fa)
        {
            dfs(to[i],u);
            siz[u]+=siz[to[i]];
            chk(--a[siz[to[i]]]);
        }
    chk(--a[n-siz[u]]);
}

inline void NTT(ll *a,int n,bool f)
{
    for(int i=0;i<n;i++)
        if(i<rev[i])
            swap(a[i],a[rev[i]]);
    for(int h=2;h<=n;h<<=1)
    {
        ll w=qpow(5,(md-1)/h);
        if(f)w=qpow(w,md-2);
        for(int j=0;j<n;j+=h)
        {
            ll wn=1;
            for(int k=j;k<j+(h>>1);k++)
            {
                ll x=a[k],y=wn*a[k+(h>>1)]%md;
                a[k]=(x+y)%md;
                a[k+(h>>1)]=(x-y+md)%md;
                wn=wn*w%md;
            }
        }
    }

    if(f)
        for(ll i=0,invn=qpow(n,md-2);i<n;i++)
            a[i]=a[i]*invn%md;
}

inline void init()
{
    fac[0]=1;
    for(ll i=1;i<N;i++)
        fac[i]=fac[i-1]*i%md;
    inv[N-1]=qpow(fac[N-1],md-2);
    for(ll i=N-1;i>=1;i--)
        inv[i-1]=inv[i]*i%md;
}

int main()
{
    init();
    n=read();
    for(int i=1,u,v;i<n;i++)
    {
        u=read();v=read();
        add(u,v);add(v,u);
    }
    dfs(1,1);

    for(int i=1;i<=n;i++)
        a[i]=a[i]*fac[i]%md;
    for(int i=1;i<=n;i++)
        b[i]=inv[n-i];

    for(m=1,l=0;m<=(n<<1);m<<=1)l++;
    for(int i=0;i<m;i++)
        rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));

    NTT(a,m,0);NTT(b,m,0);
    for(int i=0;i<m;i++)
        a[i]=a[i]*b[i]%md;
    NTT(a,m,1);

    for(int i=1;i<=n;i++)
        printf("%lld\n",a[n+i]*inv[i]%md);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值