神他吗一天考一道码农题两道 FFT(其实还是我推式子一窍不通)

题意

  给你一棵 \(n\) 个点的树,再给你一个常数 \(k\)。

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

  \(n\) 个点选 \(k\) 个点一共有 \(\tbinom{n}{k}\) 种方案,请求出所有方案的 \(f(S)\) 之和。

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

  对于 \(27\%\) 的数据,\(n\le 2700\)

  对于 \(100\%\) 的数据,$ n\le 2\times 10^5$

题解

  这种题的关键点在于 \(n^2\) \(\text{dp}\)。

  首先无法对于每个 \(k\) 快速求出答案,但我们可以考虑一个点对每个 \(k\) 的贡献。

  把 \(x\) 转为整棵树的根,则一个点 \(x\) 在这个连通子图内,当且仅当这 \(k\) 个点不都在 \(x\) 的某一个儿子子树里。

  那么贡献为 \(\tbinom{n}{k}-\sum\tbinom{a_i}{k}\),其中 \(a_i\) 为以 \(x\) 为根时,各个儿子子树的大小。

  不难发现,计算总的贡献时,\(\tbinom{n}{k}\) 会被计算 \(n\) 次,每条边两端的子树大小都会被计算一次。

  设 \(num_i\) 表示大小为 \(i\) 的子树被计算的次数(不管加还是减),则 $$b_i = \begin{cases} n\space\space\space\space\space\space\space\space\space\space\space (i=n) \ -num_i\space (i≠n) \end{cases}$$

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

  由于一棵子树被且仅被被计算一次,所以 $$\begin{align} ans_k&= \sum\limits_{i\ge k} \tbinom{i}{k} b_i \nonumber \ &= \sum\limits_{i\ge k} \frac{i!}{(i-k)!k!} b_i \nonumber \ &= \frac{1}{k!} \sum\limits_{i\ge k} b_i i!\times \frac{1}{(i-k)!} \nonumber \end{align}$$

  此时直接计算是 \(O(n^2)\) 的,可以得到 \(27\) 分。

  我们发现 \(ans\) 像一个可以卷积的生成函数。

  又发现 \(\sum\limits_{i\ge k} b_i i!\times \frac{1}{(i-k)!} = [b_k k! \frac{1}{0!}] + [b_{k+1} (k+1)! \frac{1}{1!}] \cdots + [b_n n! \frac{1}{(n-k)!}]\)

  故我们需要构造一个序列 \(c\),使得 \(c_{0\cdots (n-k)}\) 包含了 \(b_k k!,\space b_{k+1} (k+1)!,\space\cdots ,\space b_n n!\);再构造一个序列 \(d\),使得 \(d_{0\cdots (n-k)}\) 包含了 \(\frac{1}{0!},\space \frac{1}{1!},\space \cdots,\space \frac{1}{(n-k)!}\)。我们还要使一个序列的 \(i\) 由大变小,另一个序列的 \(i\) 由小变大,这样才卷积时才会把原序列中相同位置乘起来。

  于是不难构造出 $$\begin{align} c_{n-i} &= b_i i! \nonumber \ d_i &= \frac{1}{i!} \nonumber \ a_i &= \sum\limits_{j+k=i} c_j d_k \nonumber \ ans_i &= \frac{a_{n-i}}{i} \nonumber \end{align}$$

  \(\text{NTT}\) 计算出序列 \(a\) 即可。

  复杂度 \(O(n\log n)\)。

#include<bits/stdc++.h>
#define ll long long
#define N 800010
#define mod 924844033
#define G 5
#define invG 554906420
using namespace std;
inline int read(){
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c); c=getchar()) if(c=='-') f=0;
for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+(c^'0');
if(f) return x;
return 0-x;
}
int n;
struct edge{int v,nxt;}e[N<<1];
int hd[N],cnt;
inline void add(int u, int v){e[++cnt]=(edge){v,hd[u]}, hd[u]=cnt;}
int Pow(int x, int y){
int ret=1;
while(y){
if(y&1) ret=(ll)ret*x%mod;
x=(ll)x*x%mod;
y>>=1;
}
return ret;
}
int siz[N],num[N];
void dfs(int u, int fa){
siz[u]=1;
for(int i=hd[u]; i; i=e[i].nxt) if(e[i].v!=fa){
dfs(e[i].v,u);
siz[u]+=siz[e[i].v];
--num[siz[e[i].v]], --num[n-siz[e[i].v]];
}
}
int jc[N],jcn[N];
int a[N],b[N],c[N];
struct Poly{
int n,bit,r[N];
void init(int x){
for(n=1, bit=0; n<=x; n<<=1, ++bit);
for(int i=1; i<n; ++i) r[i] = (r[i>>1]>>1) | ((i&1)<<(bit-1));
}
void dft(int *a, int f){
for(int i=0; i<n; ++i) if(i<r[i]) swap(a[i],a[r[i]]);
for(int i=1; i<n; i<<=1){
int wn = Pow(f==1 ? G : invG, (mod-1)/(i<<1));
for(int j=0; j<n; j+=(i<<1)){
int w=1, x, y;
for(int k=0; k<i; ++k, w=(ll)w*wn%mod)
x=a[j+k], y=(ll)w*a[j+i+k]%mod,
a[j+k]=(x+y)%mod, a[j+i+k]=(x-y+mod)%mod;
}
}
if(f==-1){
int tmp=Pow(n,mod-2);
for(int i=0; i<n; ++i) a[i]=(ll)a[i]*tmp%mod;
}
}
}NTT;
int main(){
n=read();
int u,v;
for(int i=1; i<n; ++i) u=read(), v=read(), add(u,v), add(v,u);
jc[0]=1;
for(int i=1; i<=n; ++i) jc[i]=(ll)jc[i-1]*i%mod;
jcn[n]=Pow(jc[n],mod-2);
for(int i=n-1; i>=0; --i) jcn[i]=(ll)jcn[i+1]*(i+1)%mod;
dfs(1,0);
num[n]=n;
for(int i=0; i<=n; ++i)
b[i]=((ll)num[n-i]*jc[n-i]%mod+mod)%mod,
c[i]=jcn[i];
NTT.init(n*2+1), NTT.dft(b,1), NTT.dft(c,1);
for(int i=0; i<NTT.n; ++i) a[i]=(ll)b[i]*c[i]%mod;
NTT.dft(a,-1);
for(int i=1; i<=n; ++i) printf("%lld\n",(ll)a[n-i]*jcn[i]%mod);
return 0;
}

【AGC005 F】Many Easy Problems的更多相关文章

  1. 【AGC 005F】Many Easy Problems

    Description One day, Takahashi was given the following problem from Aoki: You are given a tree with ...

  2. 【期望DP】BZOJ3450- Tyvj1952 Easy

    ---恢复内容开始--- [题目大意] 有n次点击要做,成功了就是o,失败了就是x,分数是按comb计算的,连续a个comb就有a*a分,comb就是极大的连续o.求期望分数. [思路] 比之前的OS ...

  3. 【Hello 2018 D】Too Easy Problems

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 可以考虑把所有的题目按照ai排序. 然后顺序考虑最后做出来的题目个数和第i道题目的ai一样. 则1..i-1这些题目就没有用了. 值 ...

  4. 【AGC005F】Many Easy Problems FFT 容斥原理

    题目大意 给你一棵树,有\(n\)个点.还给你了一个整数\(k\). 设\(S\)为树上某些点的集合,定义\(f(S)\)为最小的包含\(S\)的联通子图的大小. \(n\)个点选\(k\)个点一共有 ...

  5. 【AGC005F】Many Easy Problems (NTT)

    Description ​ 给你一棵\(~n~\)个点的树和一个整数\(~k~\).设为\(~S~\)为树上某些点的集合,定义\(~f(S)~\)为最小的包含\(~S~\)的联通子图的大小.\(~n~ ...

  6. 【AGC005F】Many Easy Problems

    Description 题目链接 对于每个\(k\),统计任选\(k\)个点作为关键点的"最小生成树"的大小之和 Solution 正向想法是枚举或者计算大小为\(x\).叶子数目 ...

  7. 【hdu6334】【2018Multi-University-Training Contest04】Problem C. Problems on a Tree

    维护1边的联通块和2边的联通块,合并的时候直接启发式合并. cdqz的大爷好强啊. #include<bits/stdc++.h> #define lson (o<<1) #d ...

  8. 【SDOI2018】反回文串(【ARC064 F】Rotated Palindromes 加强版)

    题意 给你一个正整数 \(n\),求有多少字符集为 \(1\) 到 \(k\) 之间整数的字符串,使得该字符串可以由一个长度为 \(n\) 的回文串循环移位得到. ARC原题 \(100\%\) 的数 ...

  9. 【POJ 2826】An Easy Problem?!(几何、线段)

    两个木条装雨水能装多少. 两线段相交,且不遮盖的情况下才可能装到水. 求出交点,再取两线段的较高端点的较小值h,(h-交点的y)为三角形的高. 三角形的宽即为(h带入两条线段所在直线得到的横坐标的差值 ...

随机推荐

  1. Docker学习笔记 — Docker私有仓库搭建

    Docker学习笔记 — Docker私有仓库搭建   目录(?)[-] 环境准备 搭建私有仓库 测试 管理仓库中的镜像 查询 删除 Registry V2   和Mavan的管理一样,Dockers ...

  2. vue项目中使用组件化开发

    最近在使用vue-cli结合webpack打包工具开发一个后台管理系统,使用vue难免需要运用组件化思想,而这也正是vue的一大特点. 在之前做的vue项目中,稍微有一点组件化的思想,可能是对组件化不 ...

  3. VS快捷键操作

    1.窗口快捷键记忆诀窍: 凡跟窗口挂上钩的快捷键必有一个W(Windows):Ctrl+W,W: 浏览器窗口 (浏览橱窗用有道的翻译是window shopping) Ctrl+W,S: 解决方案管理 ...

  4. configparser模块,subprocess 模块,xlrd,xlwt ,xml 模块,面向对象

    1. configparser模块 2.subprocess 模块 3.xlrd,xlwt 4.xml 模块 5.面向对象 面向对象是什么? 是一种编程思想,指导你如何更好的编写代码 关注点在对象 具 ...

  5. Linux 操作命令简

    一.Linux命令及获取帮助 1.Linux命令的格式1)了解Linux命令的语法格式:命令 [选项] [参数]2)掌握命令格式中命令.选项.参数的具体含义a)命令:告诉Linux(UNIX)操作系统 ...

  6. 【转贴】GS464/GS464E

    GS464/GS464E GS464为四发射64位结构,采用动态流水线.其1.0版本(简称GS464)为9级流水线结构,在龙芯3A.3B.2H中使用.其2.0版本(简称GS464E)为12级动态流水线 ...

  7. 【面试向】hihoCoder 1994 树与落叶

    题目链接 Implementation int n, q; scan(n,q); vi p(n + 1); vi nson(n + 1); up (i, 1, n) { scan(p[i]); nso ...

  8. json与String的转化

    String转成jsonObject    JsonObject   json = JsonObject.fromObject(String str) String转成JsonArray      J ...

  9. opencv学习之读取图像-imread函数

    序 想要完整全面地学习opencv,仅凭阅读samples的示例源码是不够的.毕竟opencv是一个拥有非常多函数的程序库,所以在每学习一个函数时,芒果觉得有必要记录下来,分享给有需要的同学.于是,就 ...

  10. gunicorn 介绍与性能分析

    阅读此文前建议先阅读 我的博客 gunicorn 是一个 python wsgi http server,只支持在 unix 系统上运行 安装 gunicorn 其实是 python 的一个包,安装方 ...