CF Gym102978D Find the LCA
Description
给定一个长度为 的序列 ,对于所有 个满足 的树,求
Solution
先对于每个 计算一个大小为 的树,有多少树的形态使得树里面标号最大的两个点的 LCA 是树根
先考察一个单独的 ,容斥如果两个点选在了一个子树的情况,对于一个大小为 的子树会有 个方案
暴力的想法就是枚举在所有形态中大小为 的子树出现了多少次,乘上自己形态数量,其它点的选择方案和选择标号的方案 从总方案中减去即可,结果表达为:
后面的求和可以卷积处理,所以可以做到线对预处理 查询
原来是这样子,那么场上很多人过就能解释了
给出结论:在 时上面表达式的值为 ,证明考虑在合法方案和不合法方案中构造双射
我的构造是将一棵不合法的树重定根为 ,这样子原来 LCA(k,k-1) 现在是 LCA(1,k-1),将这个 LCA 和其 向子树砍下来接到 那里,如果 LCA 是 就只把这个点砍下来
看起来是题解的逆过程不过问题不大,那么至此表达式如下
前缀的 表示这个点作为子树根的时候它还得连接上 的点,最后简记求解中的分治 的过程:
分治过程维护两个多项式 分别表示 只乘 中的 的乘积的和 以及区间 的乘积,最后的答案就是
合并区间时 ,
感觉可能受制于分治乘法的套路式了,这个是稍加思考就能得到的
Code
inline pair<poly,poly> solve(int l,int r){
if(r<l) return {{0},{0}};
if(l==r){
poly F={0,mul(l-1,a[l])},G={1,a[l]};
return make_pair(F,G);
} int mid=(l+r)>>1;
pair<poly,poly>ls=solve(l,mid),rs=solve(mid+1,r);
return make_pair(Plus(Mul(ls.fir,rs.sec),rs.fir),Mul(ls.sec,rs.sec));
}
signed main(){
fac[0]=1; rep(i,1,250000) fac[i]=mul(fac[i-1],i);
n=read(); rep(i,1,n) a[i]=read();
int ans=fac[n-2],Mult=1;
rep(i,1,n-2) ckmul(Mult,a[i]);
ckadd(ans,mul(Mult,mul(inv2,fac[n-1])));
poly F=solve(2,n-2).fir;
for(int i=3;i<n;++i){
int val=mul(inv2,mul(fac[i-1],fac[n-i-1]));
ckadd(ans,mul(F[i-2],val));
}
print(mul(mul(a[n],a[n-1]),ans));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律