[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))\ ...
随机推荐
- 1003 Emergency Dijkstra
这题做的心很累,我用的还是 1018的思路做的,但是 使用dfs 求最大人数对于某些有问题(现在也不知道错哪了), 看了别人的代码后才发现,其实完全不用这么麻烦,只需设置一个点的权重,一遍DJ(自创简 ...
- HTML之 一 标签
一 ,标签分类: 1.普通标签: <h1> hello </h1> hello 2.自闭和标签 <hr/> 二,书写html注意事项 1.标签不能交叉嵌套 2. 标 ...
- 数据库导出sql
mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u wcnc -p smgp_apps_wcnc > wcnc.sql
- JDBC数据库
JDBC是Java程序连接和存取数据库的应用程序接口(API),包括两个包:java.sql和javax.sql. 用JDBC访问数据库的一般步骤: 1.建立数据源 2.装入JDBC驱动程序:使用Cl ...
- C++11常用特性的使用经验总结
转自:http://www.cnblogs.com/feng-sc C++11已经出来很久了,网上也早有很多优秀的C++11新特性的总结文章,在编写本博客之前,博主在工作和学习中学到的关于C++11方 ...
- 我的C#跨平台之旅(三):使用Entity Framework开发REST API
添加NuGet引用:EntityFramework 创建实体类:Book.Author 以及数据传输类:BookDTO.BookDetailDTO,代码如下: 创建DbContext类:BookDbC ...
- JVM之垃圾收集器与内存分配回收策略(二)
上一篇JVM垃圾收集器与内存分配策略(一),下面是jdk1.7版本的垃圾收集器之间的关系,其中连线两端的两种垃圾收集器可以进行搭配使用,下面来总结一下这些收集器的一些特点以及关系. 一.Serial收 ...
- C语言窗口例子
#include <windows.h> LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //声明用来处理消息的函数 in ...
- win7中调试Hotspot
预置: 1.Visual Studio 2010 2.CYGWIN(设置到path中) 3.jdk(设置到path中) 4.Framework4(系统如果有就不需要安装) 5.win7 sdk 7.1 ...
- ElasticSearch写入数据的工作原理是什么?
面试题 es 写入数据的工作原理是什么啊?es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗? 面试官心理分析 问这个,其实面试官就是要看看你了解不了解 es 的一些基 ...