【AGC005F】Many Easy Problems FFT 容斥原理
题目大意
给你一棵树,有\(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\)次。
设
\begin{cases}
n~~~~~~~~~~(i=n)\\
num_i~~~(i\neq n)
\end{cases}
\]
其中\(num_i\)为大小为\(i\)的子树的个数
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}
\]
这可以转化成卷积的形式
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 容斥原理的更多相关文章
- AGC005F Many Easy Problems(NTT)
先只考虑求某个f(k).考虑转换为计算每条边的贡献,也即该边被所选连通块包含的方案数.再考虑转换为计算每条边不被包含的方案数.这仅当所选点都在该边的同一侧.于是可得f(k)=C(n,k)+ΣC(n,k ...
- 【AtCoder】AGC005F - Many Easy Problems
题解 我们把一个点的贡献转化为一条边的贡献,因为边的数量是点的数量-1,最后再加上选点方案数\(\binom{n}{k}\)即可 一条边的贡献是\(\binom{n}{k} - \binom{a}{k ...
- [AGC005F] Many Easy Problems
link 题意简述 给定一颗无根树,对于所有大小为 $i$ 的点集,求出能够包含它的所有联通块之和,定义为 $f_i$ ,答案对 $924844033$ 取模. $n\leq 2\times 10^5 ...
- 解题:AT2064 Many Easy Problems&EXNR #1 T3 两开花
题面 两道题比较像,放在一起写了,后者可以看成前者的加强版 (sto ztb orz) 先看AT那道题 考虑计算每个点的贡献,用容斥计算:每个点没有贡献当且仅当选的所有点都在以他为根时的一个子节点的子 ...
- Codeforces 913D - Too Easy Problems
913D - Too Easy Problems 思路:二分check k 代码: #include<bits/stdc++.h> using namespace std; #define ...
- 【AtCoder】AGC005 F - Many Easy Problems 排列组合+NTT
[题目]F - Many Easy Problems [题意]给定n个点的树,定义S为大小为k的点集,则f(S)为最小的包含点集S的连通块大小,求k=1~n时的所有点集f(S)的和取模92484403 ...
- 【CodeForces】913 D. Too Easy Problems
[题目]D. Too Easy Problems [题意]给定n个问题和总时限T,每个问题给定时间ti和限制ai,当解决的问题数k<=ai时问题有效,求在时限T内选择一些问题解决的最大有效问题数 ...
- AtcoderGrandContest 005 F. Many Easy Problems
$ >AtcoderGrandContest \space 005 F. Many Easy Problems<$ 题目大意 : 有一棵大小为 \(n\) 的树,对于每一个 \(k \i ...
- 【bzoj3771】Triple FFT+容斥原理
题目描述 樵夫的每一把斧头都有一个价值,不同斧头的价值不同.总损失就是丢掉的斧头价值和. 他想对于每个可能的总损失,计算有几种可能的方案. 注意:如果水神拿走了两把斧头a和b,(a,b)和(b,a)视 ...
随机推荐
- nginx 之 proxy_redirect详解
proxy_redirect 语法:proxy_redirect [ default|off|redirect replacement ] 默认值:proxy_redirect default 使 ...
- tornado设置cookie并加密
最近看看tornado,遇到了sso的东西,了解下如何设置cookie 1.基本cookie set_cookie 方法在用户的浏览中设置 cookie: get_cookie 方法在用户的浏览中获取 ...
- 软件工程启程篇章:C#和四则运算生成与运算
0x01 :序言 I leave uncultivated today, was precisely yestoday perishes tomorrow which the person of th ...
- Python文本处理
文本处理 (一)对文本操作的流程: 打开文件,得到文件句柄并赋值给一个变量 通过句柄对文件进行操作 关闭文件 open(file, mode='r', buffering=None, encoding ...
- Linux下查看文件系统磁盘使用
[root@localhost ~]# df -h 可以查看所有文件系统的磁盘使用情况 du --max-depth=1 -h 可以查看当前目录下各子目录的磁盘使用情况 参考:http://www.2 ...
- VMware威睿
VMware总部位于美国加州帕洛阿尔托 [1] ,是全球云基础架构和移动商务解决方案厂商,提供基于VMware的解决方案, 企业通过数据中心改造和公有云整合业务,借助企业安全转型维系客户信任 [2- ...
- ocrosoft 1015 习题1.22 求一元二次方程a*x^2 + b*x + c = 0的根
http://acm.ocrosoft.com/problem.php?id=1015 题目描述 求一元二次方程a*x2 + b*x + c = 0的根.系数a.b.c为浮点数,其值在运行时由键盘输入 ...
- JavaScript生成二维码图片
1.引入一个二维码工具的js文件,同时需要引入jquery文件 下面是jquery.qrcode.min.js文件内容: (function(r){r.fn.qrcode=function(h){va ...
- WIndows下使用Grafana+InfluxDB打造监控系统
前言 对于一个运维DBA来说,了解数据库的TPS.QPS很有必要(QPS:每秒查询数,即对数据库每秒的DML的操作数:TPS:每秒事物处理,即对数据库每秒DDL操作数),通过了解他们,可以掌握一个实 ...
- C++中String类的字符串分割实现
最近笔试,经常遇到需要对字符串进行快速分割的情景,主要是在处理输入的时候,而以前练习算法题或笔试,很多时候不用花啥时间考虑测试用例输入的问题.可是C++标准库里面没有像java的String类中提供的 ...