[EOJ629] 两开花
Description
给定一棵以 \(1\) 为根 \(n\) 个节点的树。
定义 \(f(k)\) :从树上等概率随机选出 \(k\) 个节点,这 \(k\) 个点的虚树大小的期望。
一个点 \(x\) 在这些被选出的 \(k\) 个点的虚树上,当且仅当它满足下列条件至少一个:
- \(x\) 被选出。
- 存在两个被选出的节点 \(a,b\),使得 \(\operatorname{lca}(a,b)=x\)。
给定 \(m\),求 \(f(1),f(2),\cdots,f(m)\)。 对 \(998244353\) 取模。\(n\leq 4\cdot 10^5\)。
Sol
又是套着期望皮的计数题。
对于每个点 \(i\) 求出有多少种方案对答案有贡献即可:
- \(i\) 被选出,总方案数为 \(C(n-1,k-1)\) 。
- \(i\) 至少两个儿子的子树中存在被选出的点。
第二种不太好算,考虑用总方案数减去不合法的方案数。
总方案数就是 \(C(n-1,k)\)。
如果点 \(i\) 的子树中没有被选中的,方案数为 \(C(n-sze[i],k)\)。
只有一个儿子的子树中有被选中的,可以枚举儿子 \(j\),方案数就是 \(\sum\limits_{j} C(n-sze[i]+sze[j],k)\)。
注意到这样的话,\(i\) 子树中没有被选中的方案数被多算了 儿子个数次,所以还需要加上 \(son[i]\times C(n-sze[i],k)\)。
所以
\]
\]
如何对于每个 \(k\) 快速求呢?
观察到式子中的每一项组合数的上标都是 \(k\),所以我们可以开个桶 \(buc[i]\),在形如 \(buc[n-sze[i]]\) 的地方加上 \(son[i]+1\),在 \(buc[n-sze[i]+sze[j]]\) 处 \(-1\)。
好处就是,再推一步式子:
\]
这就是个卷积的形式,\(\mathbf{NTT}\)优化就吼了。
Code
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using std::min;
using std::max;
using std::swap;
using std::vector;
typedef double db;
typedef long long ll;
#define pb(A) push_back(A)
#define pii std::pair<int,int>
#define all(A) A.begin(),A.end()
#define mp(A,B) std::make_pair(A,B)
const int N=2e6+5;
const int mod=998244353;
int son[N],sze[N],buc[N];
int n,m,cnt,head[N],fac[N];
int a[N],b[N],lim,rev[N],ifac[N];
struct Edge{
int to,nxt;
}edge[N<<1];
void add(int x,int y){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
head[x]=cnt;
}
int ksm(int a,int b=mod-2,int ans=1){
while(b){
if(b&1) ans=1ll*ans*a%mod;
a=1ll*a*a%mod;b>>=1;
} return ans;
}
void ntt(int *f,int g){
for(int i=1;i<lim;i++) if(i<rev[i]) swap(f[i],f[rev[i]]);
for(int mid=1;mid<lim;mid<<=1){
int tmp=ksm(g,(mod-1)/(mid<<1));
for(int R=mid<<1,j=0;j<lim;j+=R){
for(int w=1,k=0;k<mid;k++,w=1ll*w*tmp%mod){
int x=f[j+k],y=1ll*w*f[j+k+mid]%mod;
f[j+k]=(x+y)%mod,f[j+k+mid]=(mod+x-y)%mod;
}
}
} if(g>3)
for(int in=ksm(lim),i=0;i<lim;i++) f[i]=1ll*f[i]*in%mod;
}
int getint(){
int X=0,w=0;char ch=getchar();
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=X*10+ch-48,ch=getchar();
if(w) return -X;return X;
}
void init(int n){
fac[0]=ifac[0]=1;
for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=ksm(fac[n]);
for(int i=n-1;i;i--) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
}
void dfs(int now,int fa=0){
sze[now]=1; int tot=0; buc[n]++;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(sze[to]) continue;
tot++; dfs(to,now);
sze[now]+=sze[to];
}
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(to==fa) continue;
(buc[n-sze[now]+sze[to]]+=mod-1)%=mod;
} (buc[n-sze[now]]+=tot-1+mod)%=mod;
}
int C(int n,int m){
if(n<m) return 0;
return 1ll*ifac[n]*fac[m]%mod*fac[n-m]%mod;
}
signed main(){
n=getint(),m=getint(),init(N-5);
for(int i=1;i<n;i++){
int x=getint(),y=getint();
add(x,y),add(y,x);
} dfs(1);
lim=1;while(lim<=n+n) lim<<=1;
for(int i=1;i<lim;i++) rev[i]=(rev[i>>1]>>1)|(i&1?lim>>1:0);
for(int i=0;i<=n;i++)
a[n-i]=1ll*buc[i]*fac[i]%mod,
b[i]=ifac[i];
ntt(a,3),ntt(b,3);
for(int i=0;i<lim;i++) a[i]=1ll*a[i]*b[i]%mod;
ntt(a,(mod+1)/3);
for(int i=1;i<=m;i++)
printf("%lld\n",1ll*a[n-i]*ifac[i]%mod*C(n,i)%mod);
return 0;
}
[EOJ629] 两开花的更多相关文章
- 解题:AT2064 Many Easy Problems&EXNR #1 T3 两开花
题面 两道题比较像,放在一起写了,后者可以看成前者的加强版 (sto ztb orz) 先看AT那道题 考虑计算每个点的贡献,用容斥计算:每个点没有贡献当且仅当选的所有点都在以他为根时的一个子节点的子 ...
- THU-CCF WC2019两开花记
今年年初,清华大学举办的THUWC2019即将正式开启,我将继续扮演蒟蒻OIER,努力创造一个菜鸡的形象,THU-CCF WC两爆炸,笑掉各位大牙,大家多多关注. Day0 广州好热啊╰(‵□′)╯! ...
- 【巨杉数据库SequoiaDB】企业级和开源领域“两开花”,巨杉引领国产数据库创新
2019年12月15日,OSC 源创会·年终盛典在深圳圆满举行.巨杉数据库作为业界领先的金融级分布式数据库厂商, 获得 “2019年开源数据库先锋企业” 及 “2019 GVP-Gitee最有价值开源 ...
- 为什么需要Docker?
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 估计大家也可能听过Docker这项技术(在论坛上.招 ...
- [转帖]Windows 上面IE的历史
微软向Chrome举手投降 这么多代IE你都用过吗 2019年04月20日 18:48 4030 次阅读 稿源:太平洋电脑网 2 条评论 这个清明假节,很多人过得波澜不惊,然而一个曾被万千网民挂在口中 ...
- 前后端交互实现(nginx,json,以及datatable的问题相关)
1.同源问题解决 首先,在同一个域下搭建网络域名访问,需要nginx软件,下载之后修改部分配置 然后再终端下cmd nginx.exe命令,或者打开nginx.exe文件,会运行nginx一闪而过, ...
- OO第二次博客作业(第二单元总结)
在我开始写这次博客作业的时候,窗外响起了希望之花,由此联想到乘坐自己写的电梯FROM-3-TO--1下楼洗澡,然后······ 开个玩笑,这么辣鸡的电梯肯定不会投入实际使用的,何况只是一次作业.还是从 ...
- 【CZYZ 20160819】背包
题目描述 蛤布斯有nn个物品和一个大小为mm的背包,每个物品有大小和价值,它希望你帮它求出背包里最多能放下多少价值的物品. 输入数据 第一行两个整数 n,mn,m. 接下来 nn 行每行两个整数 xi ...
- WC2019 划水记
写在前面: 本篇是擅长咕咕咕的\(\text{BLUESKY007}\)同学难得不咕写的游记,将会记录\(WC2019(2019.1.24(Day\ 0)\sim2019.1.30(Day\ 6))\ ...
随机推荐
- Day03(黑客成长日记)------>元祖及列表的增减改查
#昨日作业解析: # s = 'sadagwa'# i = 0# while i < len(s):# s1 = s[i]# print(s1)# i += 1# while使用技巧,先找递增变 ...
- 基于fpga的vga学习(2)
本次学习主要向配合basys2实行. 上次学习vga的rgb三个output都是1位的,但是我看了basys2的rgb分别是332位,这里让我卡顿了很久, 之后通过查资料才知道,rgb的不同大小表示的 ...
- 图像处理及opencv汇总
OPENCV——C++ 1.windows基于vs2017的opencv安装 2.为opencv添加contrib库 3.opencv源码编写规则 4.OpenCV库框架结构 5.OpenCV从2到3 ...
- memcache启动报错:memcached: error while loading shared libraries: libevent-XXXXX5: cannot 。。。。
创建连接 ln -s /usr/lib/libevent-2.1.so.6 /usr/lib/libevent-2.1.so.6 如果还不行就下面解决 执行下面语句查看链接地址 LD_DEBUG=l ...
- opencl 参考源码及benchmark
转载:https://www.zhihu.com/question/25539755/answer/44917891 CUDA 5之前的版本有OpenCL的sample,可以上网找找看 AMD APP ...
- 背水一战 Windows 10 (118) - 后台任务: 后台下载任务(任务分组,并行或串行执行,组完成后通知)
[源码下载] 背水一战 Windows 10 (118) - 后台任务: 后台下载任务(任务分组,并行或串行执行,组完成后通知) 作者:webabcd 介绍背水一战 Windows 10 之 后台任务 ...
- Android语音识别
语音识别 - 科大讯飞 开放平台 http://open.voicecloud.cn/ 需要拷贝lib.assets.并在清单文件中写一些权限 public class MainActivity ex ...
- Ubuntu18.04或者Deepin15.8 部署Django项目
一.首先先安装nginx静态服务 1.安装gcc g++的依赖库sudo apt-get install build-essential && sudo apt-get install ...
- 吴恩达机器学习笔记39-误差分析与类偏斜的误差度量(Error Analysis and Error Metrics for Skewed Classes)
如果你准备研究机器学习的东西,或者构造机器学习应用程序,最好的实践方法不是建立一个非常复杂的系统,拥有多么复杂的变量:而是构建一个简单的算法,这样你可以很快地实现它. 构建一个学习算法的推荐方法为:1 ...
- Python爬虫2-检测编码(使用chardet)
GitHub代码练习地址:https://github.com/Neo-ML/PythonPractice/blob/master/SpiderPrac02_chardet.py 网页编码问题解决 c ...