Codeforces 1010F - Tree(分治 NTT+树剖)
神仙题。
首先我们考虑按照这题的套路,记 \(t_i\) 表示 \(i\) 上的果子数量减去其儿子果子数量之和,那么对于一个合法的放置果子的方案,必然有 \(t_i\ge 0,\sum\limits_{i=1}^nt_i=X\),而根据隔板法,对于一个大小为 \(s\) 的连通块,分配该连通块中的 \(t_i\) 的方案数为 \(\dbinom{X+s-1}{s-1}\),因此假设大小为 \(i\) 的连通块个数为 \(c_i\),那么答案即为 \(\sum\limits_{i=1}^nc_i\dbinom{X+i-1}{i-1}\),后面那坨组合数显然可以递推求出,因此我们的任务就是求出 \(c_i\)。
考虑树形 DP,\(dp_{i,j}\) 表示 \(i\) 子树内大小为 \(j\) 的连通块有多少个,那么假设 \(u,v\) 为 \(i\) 的两个儿子,那么显然有 \(dp_{i,j}=\sum\limits_{p+q=j-1}dp_{u,p}dp_{v,q}\),\(dp_{i,0}=1\)。直接做是平方的,不过注意到这东西一脸卷的样子,因此考虑设 \(F_i(x)\) 表示 \(dp_i\) 的 OGF,那么有 \(F_i(x)=F_u(x)F_v(x)·x+1\),但是直接卷依然会被卡成 \(n^2\log n\)。这时候就要用到类似于**动态 \(dp\) **的思路了,我们考虑对整棵树进行树链剖分并对于一条重链上的节点我们直接计算其链顶上的 \(dp\) 值,那么假设这条重链上的点从上至下分别是 \(p_1,p_2,p_3,\cdots,p_k\),并且对于每个 \(p_i(i\ne k)\),其不同于 \(p_{i+1}\) 的另一个儿子为 \(q_i\)(如果这样的儿子不存在那我们默认 \(q_i=0,F_{q_i}=1\)),那么显然 \(F_{p_k}=x+1,F_{p_i}=xF_{q_i}F_{p_{i+1}}+1\),递推以下可知 \(F_{p_1}=\sum\limits_{i=1}^k\prod\limits_{j=k-i+1}^kxF_{q_i}\)。因此到这里,该问题可以转化为,有 \(k\) 个生成函数 \(a_1,a_2,\cdots,a_k\),要求 \(a_1+a_1a_2+a_1a_2a_3+\cdots+a_1a_2\cdots a_k\) 的值。该问题可以分治 FFT(其实严格来说不能叫分治 FFT,因为没有 cdq 分治) 求解,具体来说当我们分治到区间 \([l,r]\) 时我们求出 \(A_{l,r}=\prod\limits_{i=l}^ra_i\),以及 \(B_{l,r}=\sum\limits_{i=l}^r\prod\limits_{j=i}^ra_j\),那么设 \(mid=\lfloor\dfrac{l+r}{2}\rfloor\),有 \(A_{l,r}=A_{l,mid}A_{mid+1,r},B_{l,r}=B_{l,mid}+A_{l,mid}B_{mid+1,r}\),分治求解即可。
至于复杂度,大概就对于每个点,其最多会对 \(\log n\) 个多项式的度产生 \(1\) 的贡献,再加上分治 FFT 的复杂度,总复杂度 \(n\log^3n\)
u1s1 vector 实现的 NTT 是真的好写,但常数是真的大……把多项式卷积里的 vector 换成数组后常数竟一下子将为原来的 \(\dfrac{1}{8}\)……
贴个稍微能看点的代码:
const int MAXN=1e5;
const int MAXP=1<<18;
const int pr=3;
const int ipr=332748118;
const int MOD=998244353;
int inv[MAXN+5];
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int rev[MAXP+5],A[MAXP+5],B[MAXP+5],C[MAXP+5];
void NTT(int *a,int len,int type){
int lg=31-__builtin_clz(len);
for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<lg-1);
for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int i=2;i<=len;i<<=1){
int W=qpow((type<0)?ipr:pr,(MOD-1)/i);
for(int j=0;j<len;j+=i){
for(int k=0,w=1;k<(i>>1);k++,w=1ll*w*W%MOD){
int X=a[j+k],Y=1ll*a[(i>>1)+j+k]*w%MOD;
a[j+k]=(X+Y)%MOD;a[(i>>1)+j+k]=(X-Y+MOD)%MOD;
}
}
}
if(!~type){
int ivn=qpow(len,MOD-2);
for(int i=0;i<len;i++) a[i]=1ll*a[i]*ivn%MOD;
}
}
vector<int> conv(vector<int> x,vector<int> y){
if(x.size()+y.size()<=16){
vector<int> res(x.size()+y.size()-1);
for(int i=0;i<x.size();i++) for(int j=0;j<y.size();j++)
res[i+j]=(res[i+j]+1ll*x[i]*y[j])%MOD;
return res;
}
int LEN=1;while(LEN<x.size()+y.size()) LEN<<=1;
for(int i=0;i<LEN;i++) A[i]=B[i]=C[i]=0;
for(int i=0;i<x.size();i++) A[i]=x[i];
for(int i=0;i<y.size();i++) B[i]=y[i];
NTT(A,LEN,1);NTT(B,LEN,1);
for(int i=0;i<LEN;i++) C[i]=1ll*A[i]*B[i]%MOD;NTT(C,LEN,-1);
vector<int> res;for(int i=0;i<x.size()+y.size()-1;i++) res.pb(C[i]);
return res;
}
int n,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;ll X;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int siz[MAXN+5],wson[MAXN+5],fa[MAXN+5];
void dfs0(int x,int f){
siz[x]=1;fa[x]=f;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dfs0(y,x);siz[x]+=siz[y];
if(siz[y]>siz[wson[x]]) wson[x]=y;
}
}
vector<vector<int> > p;
vector<int> dp[MAXN+5];
void addzero(vector<int> &v){v.insert(v.begin(),0);}
vector<int> plus_one(vector<int> v){(v[0]+=1)%=MOD;return v;}
vector<int> minus_one(vector<int> v){(v[0]+=MOD-1)%=MOD;return v;}
vector<int> plus_vec(vector<int> a,vector<int> b){
if(a.size()<b.size()) swap(a,b);b.resize(a.size(),0);
for(int i=0;i<a.size();i++) (a[i]+=b[i])%=MOD;
return a;
}
pair<vector<int>,vector<int> > solve(int l,int r){
if(l==r) return mp(plus_one(p[l]),p[l]);int mid=l+r>>1;
pair<vector<int>,vector<int> > L=solve(l,mid);
pair<vector<int>,vector<int> > R=solve(mid+1,r);
// printf("%d %d %d %d!!!\n",L.fi.size(),L.se.size(),R.fi.size(),R.se.size());
return mp(plus_vec(conv(minus_one(L.fi),R.se),R.fi),conv(L.se,R.se));
}
void dfs(int x){
if(!wson[x]) return dp[x]=vector<int>(2,1),void();
for(int y=x;y;y=wson[y]){
for(int e=hd[y];e;e=nxt[e]){
int z=to[e];if(z==fa[y]||z==wson[y]) continue;
dfs(z);
}
} p.clear();
for(int y=x;y;y=wson[y]){
bool flg=0;
for(int e=hd[y];e;e=nxt[e]){
int z=to[e];if(z==fa[y]||z==wson[y]) continue;
addzero(dp[z]);p.pb(dp[z]);flg=1;
} if(!flg) p.pb(minus_one(vector<int>(2,1)));
} assert(!p.empty());reverse(p.begin(),p.end());
dp[x]=solve(0,p.size()-1).fi;//printf("%d: ",x);
// for(int i=0;i<dp[x].size();i++) printf("%d ",dp[x][i]);
// printf("!\n");
}
int main(){
scanf("%d%lld",&n,&X);X%=MOD;
for(int i=1,u,v;i<n;i++){scanf("%d%d",&u,&v);adde(u,v),adde(v,u);}
dfs0(1,0);dfs(1);int ans=0;
for(int i=(inv[0]=inv[1]=1)+1;i<=n;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
// for(int i=0;i<dp[1].size();i++) printf("%d%c",dp[1][i]," \n"[i+1==dp[1].size()]);
for(int i=1,t=1;i<dp[1].size();i++){
ans=(ans+1ll*dp[1][i]*t)%MOD;
t=1ll*t*(X+i)%MOD*inv[i]%MOD;
} printf("%d\n",ans);
return 0;
}
Codeforces 1010F - Tree(分治 NTT+树剖)的更多相关文章
- SPOJ375Query on a tree I(树剖+线段树)(询问边)
ιYou are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...
- codeforces#1187E. Tree Painting(树换根)
题目链接: http://codeforces.com/contest/1187/problem/E 题意: 给出一颗树,找到一个根节点,使所有节点的子节点数之和最大 数据范围: $2 \le n \ ...
- SPOJ Query on a tree II (树剖||倍增LCA)(占位)
You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, ...
- Codeforces Gym 100814C Connecting Graph 树剖并查集/LCA并查集
初始的时候有一个只有n个点的图(n <= 1e5), 现在进行m( m <= 1e5 )次操作 每次操作要么添加一条无向边, 要么询问之前结点u和v最早在哪一次操作的时候连通了 /* * ...
- SCOI2016幸运数字(树剖/倍增/点分治+线性基)
题目链接 loj luogu 题意 求树上路径最大点权异或和 自然想到(维护树上路径)+ (维护最大异或和) 那么有三种方法可以选择 1.树剖+线性基 2.倍增+线性基 3.点分治+线性基 至于线性基 ...
- SPOJ Query on a tree III (树剖(dfs序)+主席树 || Splay等平衡树)(询问点)
You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose ...
- POJ3237 Tree(树剖+线段树+lazy标记)
You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbe ...
- CF 504 E —— Misha and LCP on Tree —— 树剖+后缀数组
题目:http://codeforces.com/contest/504/problem/E 快速查询LCP,可以用后缀数组,但树上的字符串不是一个序列: 所以考虑转化成序列—— dfs 序! 普通的 ...
- 【XSY3306】alpha - 线段树+分治NTT
题目来源:noi2019模拟测试赛(一) 题意: 题解: 这场三道神仙概率期望题……orzzzy 这题暴力$O(n^2)$有30分,但貌似比正解更难想……(其实正解挺好想的) 注意到一次操作实际上就是 ...
随机推荐
- 【UE4】GamePlay架构
新标签打开或者下载看大图 更新: 增加 编程子系统 Subsystem 思维导图 Character pipeline
- vue3.x异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块 vue2.x 曾经简单的异步组件 components: { AsyncComponent: () =& ...
- 《基于SIR的路边违停行为传播模型研究》
My Focus: 路边违停 行为的传播模型; 学习基于SIR XXX模型的可行性分析.建立和结论分析 Author: 左忠义,王英英,包蕴 Mind Map:
- Shadertoy 教程 Part 5 - 运用SDF绘制出更多的2D图形
Note: This series blog was translated from Nathan Vaughn's Shaders Language Tutorial and has been au ...
- IDEA免费激活至2099年教程,亲测可用
申明,本教程 Intellij IDEA 最新版激活教程,激活码均收集与网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除.如条件允许,建议大家购买正版. 以下是本人免费激活到 2099 年的 ...
- IDM使用教程:利用IDM下载百度网盘文件
IDM是什么 其实我使用IDM下载器只是为了方便网页版百度网盘直接下载大于40M文件而已,大家知道文件过大必须打开客户端才能下载,这点对于我的破电脑感觉很烦躁,每次要等待它慢悠悠打开,然后动用我的超级 ...
- Redis 客户端重试指南
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可. 在互联网服务中,特别是在云环境下,网络及硬件环境复杂,所有应用程序都可能遇到暂时性故障.暂时性故障包括瞬时的网络抖动,服务暂时不可 ...
- TCP粘"包"问题浅析及解决方案Golang代码实现
一.粘"包"问题简介 在socket网络编程中,都是端到端通信,客户端端口+客户端IP+服务端端口+服务端IP+传输协议就组成一个可以唯一可以明确的标识一条连接.在TCP的sock ...
- JS数据类型转换问题
一.数据类型的转换 数据类型的转换方法 强制转换(显示转换,主动转换) 字符转数值 parseInt(要转换的数值或变量) 转整数 从左向右依次转换,遇到第一个非数字的字符,停止转换 忽略小数点后的内 ...
- 1.在项目中使用D3.js
在项目中使用D3.js D3.js(全称:Data-Driven Documents)是一个基于数据操作文档的JavaScript库.D3帮助您使用HTML.SVG和CSS使数据生动起来.D3对web ...