【Learning】 动态树分治
简介
动态树分治整体上由点分治发展而来。
点分治是统计树上路径,而动态树分治用来统计与点有关的树上路径,比如多次询问某一些点到询问点的距离和。
前置知识就是点分治。
做法
众所周知,点分树(点分治中重心组成的树)的深度是$O(lgn)$的。
要统计树上的路径,等价为统计经过每个点的路径。那么就统计经过每个重心的路径。
拿一道题目来讲比较具体:BZOJ4012。有一棵带点权边权的树,多次询问$(u,l,r)$,表示询问点权在$[l,r]$内的点到$u$的距离和。
比如一个重心$u$,其管辖范围为$S_u$,询问的点$q$在$v$子树内:
那么应该用其它两棵子树内所有符合的点到$u$的距离和 + $q$到$u$的距离乘上其它子树内符合的点的个数,就是经过这个重心的答案。
注意到我们需要容斥:用$u$的信息斥去$v$的信息才能得到划线部分的值,这和点分治的做法大致相同。
方便点来说,$v$子树的重心$v'$,除了要像$u$一样记录$S_v'$内所有点到它的信息,还要记录$S_v'$内所有点到$u$的信息,也就是到点分父亲的信息。原理上是和点分治一模一样的容斥,只不过为了方便,应该把这个信息放在子树的重心记录。
完整做法如下:
对于点分树上的每一个重心$u$:记其管辖范围为$S_u$。
求出$S_u$内每一个点到$u$的距离值。
对$S_u$内每一个点按照点权由小至大排序,求出排序后距离值的前缀和。
对$u$的每一个后继$v$,若$v$子树的重心为$v'$,求出$S_v'$内所有点到$u$的距离值,对它们按照点权由小至大排序,求出排序后距离值的前缀和。
calc1(u,l,r)表示求$S_u$内权值为$[l,r]$的点到$u$的距离和,由于已经按照权值排序,又有前缀和,可以二分得出$l$和$r$的位置,返回前缀和。
calc2(u,l,r)表示求$S_u$内权值为$[l,r]$的点到$u$的点分父亲的距离和,求法同上。
size(u,l,r)表示求$S_u$内权值为$[l,r]$的点有多少个,求法同上二分。
dis(x,y)表示求原树上$x$~$y$的距离。
查询$(u,l,r)$
对于$u$和$u$的所有点分树祖先,记为$x$,设$u$在$x$的$y$子树中:
$ans+=dis(u,x)*[size(x,l,r)-size(y,l,r)]+calc1(x,l,r)-calc2(y,l,r)$
1 #include <cstdio> 2 #include <algorithm> 3 #include <vector> 4 #define mp make_pair 5 using namespace std; 6 typedef long long ll; 7 typedef pair<int,int> pii; 8 const int N=150010,INF=2147000000,Bas=20; 9 int n,q,A,a[N],h[N],tot; //权值和边 10 int pre[N][Bas],dep[N],dist[N]; //倍增lca 点到根节点的深度和距离 11 int cut[N],fa[N];//点分区块断点,点分树父亲 12 int size[N],mins,minu; //找重心 13 struct Edge{int v,next,w;}g[N*2]; //树边 14 vector<pii> lis[N],lisf[N]; //每个重心的数据列表 每个重心相对于点分父亲的数据列表 15 vector<ll> sum[N],sumf[N]; //每个重心列表按权值排序后距离前缀和 每个重心相对于点分父亲的列表按权值排序后距离前缀和 16 inline int rd(){ 17 int x=0; 18 char c; 19 while((c=getchar())<'0'||c>'9'); 20 x=c-'0'; 21 while('0'<=(c=getchar())&&c<='9') x=x*10+c-'0'; 22 return x; 23 } 24 inline int min(int x,int y){return x<y?x:y;} 25 inline int max(int x,int y){return x>y?x:y;} 26 inline void swap(int &x,int &y){int t=x;x=y;y=t;} 27 inline void addEdge(int u,int v,int w){ 28 g[++tot].v=v; g[tot].w=w; g[tot].next=h[u]; h[u]=tot; 29 g[++tot].v=u; g[tot].w=w; g[tot].next=h[v]; h[v]=tot; 30 } 31 void predfs(int u,int fa,int Dep,int Dist){//计算11行的数组 32 dep[u]=Dep; 33 dist[u]=Dist; 34 pre[u][0]=fa; 35 for(int i=1;i<Bas;i++) pre[u][i]=pre[pre[u][i-1]][i-1]; 36 for(int i=h[u],v;i;i=g[i].next) 37 if((v=g[i].v)!=fa) 38 predfs(v,u,Dep+1,Dist+g[i].w); 39 } 40 int getlca(int a,int b){ 41 if(dep[a]<dep[b]) swap(a,b); 42 for(int i=Bas-1;i>=0;i--) 43 if(dep[pre[a][i]]>=dep[b]) a=pre[a][i]; 44 if(a==b) return a; 45 for(int i=Bas-1;i>=0;i--) 46 if(pre[a][i]!=pre[b][i]) a=pre[a][i],b=pre[b][i]; 47 return pre[a][0]; 48 } 49 int getdis(int x,int y){ 50 int lca=getlca(x,y); 51 return dist[x]-dist[lca]+dist[y]-dist[lca]; 52 } 53 void dfs1(int u,int fa){//找重心 54 size[u]=1; 55 for(int i=h[u],v;i;i=g[i].next) 56 if(!cut[v=g[i].v]&&v!=fa){ 57 dfs1(v,u); 58 size[u]+=size[v]; 59 } 60 } 61 void dfs2(int u,int fa,int all){//找重心 62 int maxt=0; 63 for(int i=h[u],v;i;i=g[i].next) 64 if(!cut[v=g[i].v]&&v!=fa){ 65 dfs2(v,u,all); 66 if(size[v]>maxt) maxt=size[v]; 67 } 68 if(all-size[u]>maxt) maxt=all-size[u]; 69 if(maxt<mins) mins=maxt,minu=u; 70 } 71 int getroot(int u){//找重心 72 mins=INF; minu=0; 73 dfs1(u,0); 74 dfs2(u,0,size[u]); 75 return minu; 76 } 77 void collect(int u,int fa,int dis,int st,int type){//收集数据至重心st的列表 78 if(!type) lis[st].push_back(mp(a[u],dis)); 79 else lisf[st].push_back(mp(a[u],dis)); 80 for(int i=h[u],v;i;i=g[i].next) 81 if(!cut[v=g[i].v]&&v!=fa) 82 collect(v,u,dis+g[i].w,st,type); 83 } 84 int find(vector<pii> &u,int x,int type){//以权值为关键字,lowerbound或upperbound x的位置 85 int l=0,r=u.size()-1,mid; 86 while(l<=r){ 87 mid=(l+r)>>1; 88 if((!type&&x<=u[mid].first)||(type&&x<u[mid].first)) r=mid-1; 89 else l=mid+1; 90 } 91 return l; 92 } 93 ll calc(vector<pii> &u,vector<ll> &s,int l,int r,int type){//计算列表在l~r的点的距离和或个数 94 int pos1=find(u,l,0); 95 int pos2=find(u,r,1)-1; 96 if(pos2<pos1) return 0; 97 if(pos1==0){ 98 if(!type) return s[min(u.size()-1,pos2)]; 99 else return min(u.size()-1,pos2)+1; 100 } 101 if(!type) 102 return s[min(u.size()-1,pos2)]-s[max(0,pos1-1)]; 103 else 104 return min(u.size()-1,pos2)-max(0,pos1)+1; 105 } 106 int solve(int rt,int faedge){//预处理 107 int u=getroot(rt); 108 collect(u,0,0,u,0); 109 sort(lis[u].begin(),lis[u].end()); 110 ll x=0; 111 for(int i=0,sz=lis[u].size();i<sz;i++){ 112 x+=lis[u][i].second; 113 sum[u].push_back(x); 114 } 115 if(faedge){ 116 collect(rt,0,faedge,u,1); 117 sort(lisf[u].begin(),lisf[u].end()); 118 x=0; 119 for(int i=0,sz=lisf[u].size();i<sz;i++){ 120 x+=lisf[u][i].second; 121 sumf[u].push_back(x); 122 } 123 } 124 cut[u]=1; 125 for(int i=h[u],v;i;i=g[i].next) 126 if(!cut[v=g[i].v]) 127 fa[solve(v,g[i].w)]=u; 128 return u; 129 } 130 ll query(int st,int u,int l,int r,ll sonsum,int sonsize){//询问 131 ll ret=0; 132 if(fa[u]) ret=query(st,fa[u],l,r,calc(lisf[u],sumf[u],l,r,0),calc(lisf[u],sumf[u],l,r,1)); 133 ret+=1LL*getdis(st,u)*(calc(lis[u],sum[u],l,r,1)-sonsize)+calc(lis[u],sum[u],l,r,0)-sonsum; 134 return ret; 135 } 136 int main(){ 137 n=rd(); q=rd(); A=rd(); 138 for(int i=1;i<=n;i++) a[i]=rd(); 139 for(int i=1,u,v,w;i<n;i++){ 140 u=rd();v=rd();w=rd(); 141 addEdge(u,v,w); 142 } 143 predfs(1,0,1,0); 144 solve(1,0); 145 int u,l,r; 146 ll ans=0; 147 while(q--){ 148 u=rd(); l=rd(); r=rd(); 149 l=(l+ans)%A; r=(r+ans)%A; 150 if(l>r) swap(l,r); 151 ans=query(u,u,l,r,0,0); 152 printf("%lld\n",ans); 153 } 154 return 0; 155 }
转载于:https://www.cnblogs.com/RogerDTZ/p/8032484.html
【Learning】 动态树分治相关推荐
- P4719 【模板】“动态 DP“动态树分治(矩阵/轻重链剖分/ddp)
P4719 [模板]"动态 DP"&动态树分治 求解树上最大权独立集,但是需要支持修改. https://www.luogu.com.cn/problem/solution ...
- [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA]
[BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA] 题目大意: 给定一颗nn个节点的树,边权均为11,初始每个点权值为00 . 其中操作QQ xx询问x点的点权,操作 MM xx dd ...
- 【BZOJ4372】烁烁的游戏 动态树分治+线段树
[BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围 ...
- 【ZJOI2007】捉迷藏(动态树分治)
显然只有一次询问的话,可以用点分治来实现. 但是现在我们有多组询问,还带有修改,我们只能通过动态点分治来做了. 动态点分治的主要思想:省去每次点分治求重心的过程,直接预处理出来(因为树的形态不会改变) ...
- BZOJ 3730: 震波 动态树分治 线段树 lca
3730: 震波 Time Limit: 15 Sec Memory Limit: 256 MB Submit: 1202 Solved: 288 [Submit][Status][Discuss ...
- BZOJ 4012: [HNOI2015]开店 -- 动态树分治
4012: [HNOI2015]开店 Time Limit: 70 Sec Memory Limit: 512 MB Submit: 1463 Solved: 635 [Submit][Statu ...
- 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治
[BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...
- 【bzoj4372】 烁烁的游戏【动态树分治】
烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节 ...
- BZOJ 3435: [Wc2014]紫荆花之恋 【(替罪羊树式)动态点分治 + Treap】
BZOJ 传送门 洛谷传送门 题目分析: 似乎做过几道点分树的题之后题解还是比较好懂的. 这位dalao的题目分析非常的到位,Orz. PoPoQQQ的具体解读也非常的清晰,Orz. R i + R ...
- 点分治动态点分治小结
(写篇博客证明自己还活着×2) 转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8006488.html 有的时候,我们会发现这样一类题:它长得很像一个$O(n) ...
最新文章
- JavaScript 渐变效果
- HTTP 协议(详解)
- 2021下午茶数字经济蓝皮书
- iOS高德地图SDK定位和搜索附近信息的具体使用
- 点餐系统ip地址_spring boot实战——微信点餐系统03:微信授权(用户授权),免费内网穿透(固定ip)...
- 这些将在新一年改变你的风控内容
- C# winForm utf8 gbk 相互转码小工具
- 电商购物核心架构演进:谁说架构思路会过时?
- 支持SMTP邮箱介绍
- 《深入理解Windows操作系统》笔记5
- mongodb查询某个字段数据
- 第5关:类与对象练习------Java面向对象 - 类与对象
- Qt 绘制南丁格尔玫瑰图
- Java 判断对象是否所有属性为空
- 2018年苹果年费支付失败真正的原因
- css3怎么做多边形,CSS | 实现有趣的多边形
- mysql按半小时分组
- old DIB in res XXX ico pass it through SDKPAINT 错误
- Linux内核是什么?Linux内核是怎么工作的?
- 【codeforces 718E】E. Matvey's Birthday