题目大意

  给你一棵树,有\(n\)个点。还给你了一个整数\(k\)。

  设\(S\)为树上某些点的集合,定义\(f(S)\)为最小的包含\(S\)的联通子图的大小。

  \(n\)个点选\(k\)个点一共有\(\binom{n}{k}\)中方案,请你求出所有方案的\(f(S)\)的和\(\mod 924844033\)。

  出题人觉得这样就太简单了,他决定让你求出所有\(k=1\ldots n\)的答案。

  \(n\leq 200000\)

题解

  似乎对于每个\(k\)没办法快速求出答案。

  我们考虑一个点对所有答案的贡献。

  一个点\(x\)在这个联通子图内当且经当这\(k\)个点不在以\(x\)为根时\(x\)的子树内。

  那么贡献为\(\binom{n}{k}-\sum \binom{a_i}{k}\),其中\(a_i\)为以\(x\)为根时各个子树的大小。可以发现,计算总的贡献时每条边两端的子树大小都会被计算一次。\(\binom{n}{k}\)会被计算\(n\)次。

  设

\[b_i=
\begin{cases}
n~~~~~~~~~~(i=n)\\
num_i~~~(i\neq n)
\end{cases}
\]

其中\(num_i\)为大小为\(i\)的子树的个数

\[\begin{align}
ans_k&=\sum_{i\geq k} b_i\binom{i}{k}\\
&=\sum_{i\geq k} b_i\frac{i!}{(i-k)!k!}\\
&=\frac 1{k!}\sum_{i\geq k}b_ii!\times \frac{1}{(i-k)!}
\end{align}
\]

  这可以转化成卷积的形式

\[\begin{align}
c_{n-i}&=num_ii!\\
d_{i}&=\frac{1}{i!}\\
a_i&=\sum_{j+k=i}c_id_i\\
ans_i&=\frac{a_{n-i}}{i!}
\end{align}
\]

  时间复杂度:\(O(n\log n)\)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<list>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll p=924844033;
const ll g=5;
ll fp(ll a,ll b)
{
ll s=1;
while(b)
{
if(b&1)
s=s*a%p;
a=a*a%p;
b>>=1;
}
return s;
}
namespace ntt
{
ll w1[1000010];
ll w2[1000010];
int rev[1000010];
int n;
void init()
{
#ifdef DEBUG
n=16;
#else
n=524288;
#endif
int i;
for(i=1;i<=n;i<<=1)
{
w1[i]=fp(g,(p-1)/i);
w2[i]=fp(w1[i],p-2);
}
rev[0]=0;
for(i=1;i<n;i++)
rev[i]=(rev[i>>1]>>1)|(i&1?n>>1:0);
}
void ntt(ll *a,int t)
{
int i,j,k;
ll u,v,w,wn;
for(i=0;i<n;i++)
if(rev[i]<i)
swap(a[i],a[rev[i]]);
for(i=2;i<=n;i<<=1)
{
wn=(t==1?w1[i]:w2[i]);
for(j=0;j<n;j+=i)
{
w=1;
for(k=j;k<j+i/2;k++)
{
u=a[k];
v=a[k+i/2]*w%p;
a[k]=(u+v)%p;
a[k+i/2]=(u-v)%p;
w=w*wn%p;
}
}
}
if(t==-1)
{
ll inv=fp(n,p-2);
for(i=0;i<n;i++)
a[i]=a[i]*inv%p;
}
}
};
ll b[1000010];
ll c[1000010];
ll a[1000010];
ll inv[1000010];
ll fac[1000010];
ll ifac[1000010];
int s[1000010];
int n;
list<int> l[200010];
ll num[1000010];
void dfs(int x,int fa)
{
s[x]=1;
for(auto v:l[x])
if(v!=fa)
{
dfs(v,x);
s[x]+=s[v];
num[s[v]]--;
num[n-s[v]]--;
}
}
int main()
{
#ifdef DEBUG
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
scanf("%d",&n);
int i,x,y;
for(i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
l[x].push_back(y);
l[y].push_back(x);
}
inv[0]=inv[1]=fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(i=2;i<=n;i++)
{
inv[i]=-(p/i)*inv[p%i]%p;
fac[i]=fac[i-1]*i%p;
ifac[i]=ifac[i-1]*inv[i]%p;
}
dfs(1,0);
num[n]=n;
for(i=0;i<=n;i++)
b[i]=num[n-i]*fac[n-i]%p;
for(i=0;i<=n;i++)
c[i]=ifac[i];
ntt::init();
ntt::ntt(b,1);
ntt::ntt(c,1);
for(i=0;i<ntt::n;i++)
a[i]=b[i]*c[i]%p;
ntt::ntt(a,-1);
for(i=1;i<=n;i++)
{
ll ans=a[n-i]*ifac[i]%p;
ans=(ans+p)%p;
printf("%lld\n",ans);
}
return 0;
}

【AGC005F】Many Easy Problems FFT 容斥原理的更多相关文章

  1. AGC005F Many Easy Problems(NTT)

    先只考虑求某个f(k).考虑转换为计算每条边的贡献,也即该边被所选连通块包含的方案数.再考虑转换为计算每条边不被包含的方案数.这仅当所选点都在该边的同一侧.于是可得f(k)=C(n,k)+ΣC(n,k ...

  2. 【AtCoder】AGC005F - Many Easy Problems

    题解 我们把一个点的贡献转化为一条边的贡献,因为边的数量是点的数量-1,最后再加上选点方案数\(\binom{n}{k}\)即可 一条边的贡献是\(\binom{n}{k} - \binom{a}{k ...

  3. [AGC005F] Many Easy Problems

    link 题意简述 给定一颗无根树,对于所有大小为 $i$ 的点集,求出能够包含它的所有联通块之和,定义为 $f_i$ ,答案对 $924844033$ 取模. $n\leq 2\times 10^5 ...

  4. 解题:AT2064 Many Easy Problems&EXNR #1 T3 两开花

    题面 两道题比较像,放在一起写了,后者可以看成前者的加强版 (sto ztb orz) 先看AT那道题 考虑计算每个点的贡献,用容斥计算:每个点没有贡献当且仅当选的所有点都在以他为根时的一个子节点的子 ...

  5. Codeforces 913D - Too Easy Problems

    913D - Too Easy Problems 思路:二分check k 代码: #include<bits/stdc++.h> using namespace std; #define ...

  6. 【AtCoder】AGC005 F - Many Easy Problems 排列组合+NTT

    [题目]F - Many Easy Problems [题意]给定n个点的树,定义S为大小为k的点集,则f(S)为最小的包含点集S的连通块大小,求k=1~n时的所有点集f(S)的和取模92484403 ...

  7. 【CodeForces】913 D. Too Easy Problems

    [题目]D. Too Easy Problems [题意]给定n个问题和总时限T,每个问题给定时间ti和限制ai,当解决的问题数k<=ai时问题有效,求在时限T内选择一些问题解决的最大有效问题数 ...

  8. AtcoderGrandContest 005 F. Many Easy Problems

    $ >AtcoderGrandContest \space 005 F.  Many Easy Problems<$ 题目大意 : 有一棵大小为 \(n\) 的树,对于每一个 \(k \i ...

  9. 【bzoj3771】Triple FFT+容斥原理

    题目描述 樵夫的每一把斧头都有一个价值,不同斧头的价值不同.总损失就是丢掉的斧头价值和. 他想对于每个可能的总损失,计算有几种可能的方案. 注意:如果水神拿走了两把斧头a和b,(a,b)和(b,a)视 ...

随机推荐

  1. Ordering Tasks

    链接 [https://vjudge.net/contest/281085#problem/D] 题意 有n个任务,有M个对先后顺序 然你输出最后的完成任务的顺序,有多种可能输出一种即可 分析 裸的拓 ...

  2. PS绘制扁平化风格相机镜头UI图标

    一.新建一个画布,绘制一个460*460图层,圆角半径40像素.填充渐变颜色,加一点点投影,这样就有质感了. 二.接下来我们要来绘制主体部分,绘制一个圆,大小400*400,用内阴影,渐变叠加,投影得 ...

  3. Python_面向对象基础

    概念 类 一类抽象的事物,是描述了一类事物有哪些属性或者行为,但不是具体——模子. 实例 一个依托于类的规范存在的,被赋予了具体属性值的实际存在的物体. 对象 就是实例,实例的另外一个名称,相当于别名 ...

  4. Linux 典型应用之服务管理

    crontab 定时任务 用户所建立的crontab文件中,每一行都代表一项任务,每行的每个字段代表一项设置,它的格式共分为六个字段,前五段是时间设定段,第六段是要执行的命令段,格式如下: minut ...

  5. 【学亮IT手记】Java 8新特性实例介绍

    java8,也称为jdk1.8,于2014.03.18日发布,它支持函数式编程,新的js引擎,新的日期API,新的Stream Api等. 我们主要讨论以下几个新特性: ①Lambda表达式. 允许把 ...

  6. 在Laravel中使用数据库事务以及捕获事务失败后的异常

    Description 在Laravel中要想在数据库事务中运行一组操作,则可以在 DB facade 中使用 transaction 方法.如果在事务的闭包内抛出异常,事务将会被自动还原.如果闭包运 ...

  7. .Net的EF+MVC框架使用T4生成各个层的代码的,在新增表的时候,调不到新增的实体

    如果确认有这个实体的话,只需要把T4模板全部重新生成就可以了

  8. Python创建virtualenv(虚拟环境)方法

    本文目录 一 前言 二 通过virtualenv软件创建 三 在pycharm下创建 新建项目 四 已有项目使用和创建虚拟环境 五 参数说明 一 前言 需求:        --公司之有一台服务器   ...

  9. CodeForces 113B Petr#

    题目链接:http://codeforces.com/problemset/problem/113/B 题目大意: 多组数据每组给定3个字符串T,Sbeg,Sed,求字符串T中有多少子串是以Sbeg开 ...

  10. @Param注解

    关于mybatis的@Param注解和参数 引用 https://www.cnblogs.com/whisper527/p/6568028.html 薇飘意 1,使用@Param注解 当以下面的方式进 ...