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分,但貌似比正解更难想……(其实正解挺好想的) 注意到一次操作实际上就是 ...
随机推荐
- cs224n 2019
视频链接 相关资源 Notes 笔记下载 笔记2 需要挂梯子,不然不显示图片,如果用ssr,要调到全局模式 转自:bitJoy CS224N(1.8)introduction and Word Vec ...
- [CPP] 类的内存布局
本文可以解决下面 3 个问题: 以不同方式继承之后,类的成员变量是如何分布的? 虚函数表及虚函数表指针,在可执行文件中的位置? 单一继承.多继承.虚拟继承之后,类的虚函数表的内容是如何变化的? 在这里 ...
- Spring Security:如何在Postman中优雅地测试后端API(前后端分离)
前言 在Postman中可以编写和执行自动化测试,使用 JavaScript 编写基本的 API 测试,自由编写任何用于自动化测试的测试方案. 在POSTMAN中读取Cookie值 1. 我们需要向& ...
- 6.深入TiDB:乐观事务
本文基于 TiDB release-5.1进行分析,需要用到 Go 1.16以后的版本 我的博客地址:: https://www.luozhiyun.com/archives/620 事务模型概述 由 ...
- IM服务器:我的千万级在线聊天服务器集群
一.服务器特点 01.傻瓜式部署,一键式启动: 02.单机支持10万以上在线用户聊天(8G内存,如果内存足够大,并发量可超过10万): 03.支持服务器集群,集群间高内聚.低耦合,可动态横向扩展IM服 ...
- .Net(c#)汉字和Unicode编码互相转换实例
{"name": "\u676d\u5dde", "href": "www.baidu.com"} 经常遇到这样内容的j ...
- 一从二主IIC连接调试
最近有个项目需要实现快速开机出摄像头预览(2s内),但是我的板子linux上的qt应用起来都要10s左右了,于是在硬件上增加了一个屏驱芯片TW8836,这是一个mcu,可以直接获取摄像头数据送到lcd ...
- python教程-(三)使用字符串
一.设置字符串的格式:精简版 方法1 >>> format = "Hello %s, welcome to %s" >>> values = ( ...
- 服务集与AP的配合
一.实验目的 1)掌握添加无线网络配置 2)掌握配置信道和协议使用并配置在一个天线上同时运行两个服务集,即两个无线网络 二.实验仪器设备及软件 仪器设备:一台AC,两台AP,一台AR,一台LSW 软件 ...
- SpringBoot热部署(7)
1.引入热部署依赖包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId ...