Codeforces 题面传送门 & 洛谷题面传送门

神仙题。

首先我们考虑按照这题的套路,记 \(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+树剖)的更多相关文章

  1. SPOJ375Query on a tree I(树剖+线段树)(询问边)

    ιYou are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, ...

  2. codeforces#1187E. Tree Painting(树换根)

    题目链接: http://codeforces.com/contest/1187/problem/E 题意: 给出一颗树,找到一个根节点,使所有节点的子节点数之和最大 数据范围: $2 \le n \ ...

  3. 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, ...

  4. Codeforces Gym 100814C Connecting Graph 树剖并查集/LCA并查集

    初始的时候有一个只有n个点的图(n <= 1e5), 现在进行m( m <= 1e5 )次操作 每次操作要么添加一条无向边, 要么询问之前结点u和v最早在哪一次操作的时候连通了 /* * ...

  5. SCOI2016幸运数字(树剖/倍增/点分治+线性基)

    题目链接 loj luogu 题意 求树上路径最大点权异或和 自然想到(维护树上路径)+ (维护最大异或和) 那么有三种方法可以选择 1.树剖+线性基 2.倍增+线性基 3.点分治+线性基 至于线性基 ...

  6. 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 ...

  7. 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 ...

  8. CF 504 E —— Misha and LCP on Tree —— 树剖+后缀数组

    题目:http://codeforces.com/contest/504/problem/E 快速查询LCP,可以用后缀数组,但树上的字符串不是一个序列: 所以考虑转化成序列—— dfs 序! 普通的 ...

  9. 【XSY3306】alpha - 线段树+分治NTT

    题目来源:noi2019模拟测试赛(一) 题意: 题解: 这场三道神仙概率期望题……orzzzy 这题暴力$O(n^2)$有30分,但貌似比正解更难想……(其实正解挺好想的) 注意到一次操作实际上就是 ...

随机推荐

  1. Kali安装OWASP

    我是2019版的kali,里面并没有自带OWASP工具,因为OWASP不再更新的因素,所以新版kali将它移除了  安装OWASP apt-get install zaproxy #以下都是安装软件时 ...

  2. MySQL:基础语法-3

    MySQL:基础语法-3 记录一下 MySQL 基础的一些语法,便于查询,该部分内容主要是参考:bilibili 上 黑马程序员 的课程而做的笔记,由于时间有点久了,课程地址忘记了 上文MySQL:基 ...

  3. Scrum Meeting 0531

    零.说明 日期:2021-5-31 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 困难 qsy PM&前端 完成后端管 ...

  4. 2020BUAA软工个人项目作业

    2020BUAA软工个人项目作业 17373010 杜博玮 项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 个人项目作业 我在这个课程的目标是 学 ...

  5. SpringCloud 2020.0.4 系列之服务降级的其他用法与熔断

    1. 概述 老话说的好:控制好自己的情绪,才能控制好自己的人生.冲动是魔鬼,冷静才最重要. 言归正传,之前聊了在 Feign 调用时,如何给整个 Feign接口类 增加降级策略. 今天我们来聊一下 H ...

  6. Noip模拟58 2021.9.21(中秋祭&&换机房祭)

    第一次在学校过中秋节,给家里人视频电话,感觉快回家了很开心, 然后还吃了汉堡喝饮料非常爽,颓废了一会儿还换了新机房,$Linux2.0$非常dei,少爷机也非常快, 发现好像测评机又成了老爷机,这就是 ...

  7. Springboot第一次访问慢,自身缺陷问题?

    一.现象: 1.访问controller,第一次速度在300-400ms,第二次访问就很快了大概在20ms,相差几十倍,是哪里出了问题,尝试了网上很多教程都没有作用 如启动参数设置 -Djava.se ...

  8. Apache Solr应用服务器存在远程代码执行漏洞👻

    Apache Solr应用服务器存在远程代码执行漏洞 1.描述 Apache Solr是一个开源的搜索服务,使用Java语言开发,主要基于HTTP和Apache Lucene实现的. Solr是一个高 ...

  9. LoadRunner12回放与录制

    系统版本 本人的操作系统是win10 版本是loadrunner12. 开启loadrunner自带的机票预订服务器 找到loadrunner自带的机票预订测试服务器下图中点击启动 如下图所示代表启动 ...

  10. 集合之Map接口

    Map接口概述 Map与Collection并列存在.用于存储具有映射关系的数据 : key-value Map 中的 key 和 value 都可以是任何引用类型的数据 Map 中的 key 用Se ...