BZOJ3451 Normal 和 CF235D Graph Game
Normal
定义一次点分治的复杂度是所有分治中心分治时的子树大小之和。
给定一棵树,问所有点等概率被选做重心,点分治的期望复杂度。
n≤30000。
题解
https://www.cnblogs.com/SGCollin/p/10597925.html
根据期望的线性性,答案等价于每个点在点分树上的深度期望之和。
思路是从点对的角度考虑某一个点是否会产生贡献。
E(dep_x)=\sum_{y=1}^n P(x\in subtree_y)
也就是 x 在点分树上在 1\dots n 的子树中的概率和。
考虑点分树上 y 是 x 的祖先的条件,要求 x 和 y 构成的这条链上第一个在点分治过程中被删除的点是 y ,由于链上被选中的概率相等,因此这个概率为 \frac{1}{dis(x,y) + 1}。
所以答案为
\sum_{x=1}^n\sum_{j=1}^n \frac{1}{dis(i,j) + 1}\\
=\sum_{i = 0}^n \frac{cnt_i}{i + 1}
因此需要点分治求长度为 i 的路径条数 cnt_i ,注意到合并的时候是卷积的形式。
容斥做法
不考虑重复路径,把子树DFS一遍,直接自己和自己进行卷积,再去掉子树内重复计数的路径即可。
每一层最差以自己的size作为长度进行卷积,因此复杂度为 O(n\log^2 n)。
CO int N=1<<16; int omg[2][N],rev[N]; void NTT(int a[],int lim,int dir){ int len=log2(lim); for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1); for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]); for(int i=1;i<lim;i<<=1) for(int j=0;j<lim;j+=i<<1) for(int k=0;k<i;++k){ int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]); a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t); } if(dir==1){ int ilim=fpow(lim,mod-2); for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim); } } vector<int> to[N]; bool vis[N]; int siz[N],all; pair<int,int> root; void find_root(int u,int fa){ siz[u]=1; int big=0; for(int v:to[u])if(v!=fa and !vis[v]){ find_root(v,u); siz[u]+=siz[v],big=max(big,siz[v]); } big=max(big,all-siz[u]); root=min(root,make_pair(big,u)); } int buc[N],len,cnt[N]; void dfs(int u,int fa,int dep){ ++buc[dep],len=max(len,dep); for(int v:to[u])if(v!=fa and !vis[v]) dfs(v,u,dep+1); } void calc(int u,int o){ len=0,dfs(u,0,0); int lim=1<<int(ceil(log2(2*len+1))); NTT(buc,lim,0); for(int i=0;i<lim;++i) buc[i]=mul(buc[i],buc[i]); NTT(buc,lim,1); if(o>0){ for(int i=0;i<=2*len;++i) cnt[i]+=buc[i]; } else{ for(int i=0;i<=2*len;++i) cnt[i+2]-=buc[i]; } memset(buc,0,lim*sizeof(int)); } void divide(int u){ vis[u]=1; calc(u,1); int oall=all; for(int v:to[u])if(!vis[v]){ calc(v,-1); all=siz[v]<siz[u]?siz[v]:oall-siz[u],root=make_pair(all,0); find_root(v,0),divide(root.second); } } int main(){ omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N); omg[1][0]=1,omg[1][1]=fpow(332748118,(mod-1)/N); for(int i=2;i<N;++i){ omg[0][i]=mul(omg[0][i-1],omg[0][1]); omg[1][i]=mul(omg[1][i-1],omg[1][1]); } int n=read<int>(); for(int i=1;i<n;++i){ int u=read<int>()+1,v=read<int>()+1; to[u].push_back(v),to[v].push_back(u); } all=n,root=make_pair(all,0); find_root(1,0),divide(root.second); LD ans=0; for(int i=0;i<=n;++i) ans+=(LD)cnt[i]/(i+1); printf("%.4Lf\n",ans); return 0; }
看了别人的代码,才发现之前自己的预处理原根是多么智障。
Graph Game
给你⼀个 n 个点,n 条边的连通图。每次随机选⼀个点删除,然后递归所有连通分量,代价是每次连通块⼤⼩。
问代价期望。3 ≤ n ≤ 3000。
题解
https://www.cnblogs.com/yzxverygood/p/10406215.html
也就是需要对两点之间的路径经过环的概率容斥一下。
CO int N=3e3+10; vector<int> to[N]; int deg[N],vis[N],cir[N],len; int top[N],fa[N][12],dep[N]; void dfs(int u){ cir[++len]=u,vis[u]=len; for(int v:to[u])if(!vis[v]) dfs(v); } int lca(int x,int y){ if(dep[x]<dep[y]) swap(x,y); for(int i=11;i>=0;--i)if(dep[fa[x][i]]>=dep[y]) x=fa[x][i]; if(x==y) return x; for(int i=11;i>=0;--i)if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int main(){ int n=read<int>(); for(int i=1;i<=n;++i){ int u=read<int>()+1,v=read<int>()+1; to[u].push_back(v),to[v].push_back(u); ++deg[u],++deg[v]; } deque<int> Q; for(int u=1;u<=n;++u)if(deg[u]==1) Q.push_back(u); while(Q.size()){ int u=Q.front();Q.pop_front(); vis[u]=-1; for(int v:to[u])if(--deg[v]==1) Q.push_back(v); } for(int u=1;u<=n;++u)if(deg[u]==2){ dfs(u);break; } for(int u=1;u<=n;++u)if(vis[u]>0){ top[u]=u,dep[u]=1; Q.push_back(u); while(Q.size()){ int u=Q.front();Q.pop_front(); for(int i=1;i<=11;++i) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int v:to[u])if(vis[v]==-1 and v!=fa[u][0]){ top[v]=top[u],fa[v][0]=u,dep[v]=dep[u]+1; Q.push_back(v); } } } LD ans=0; for(int u=1;u<=n;++u)for(int v=1;v<=n;++v){ if(top[u]==top[v]){ int f=lca(u,v); ans+=(LD)1/(dep[u]+dep[v]-2*dep[f]+1); } else{ int x=dep[u]-dep[top[u]]+1+dep[v]-dep[top[v]]+1; int y=abs(vis[top[u]]-vis[top[v]])-1,z=len-2-y; ans+=(LD)1/(x+y)+(LD)1/(x+z)-(LD)1/(x+y+z); } } printf("%.9Lf\n",ans); return 0; }
静渊以有谋,疏通而知事。
标签:
快速傅里叶变换和快速数论变换
, 点分治
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· 程序员常用高效实用工具推荐,办公效率提升利器!
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)
2018-12-27 斜率优化DP总结