简介

  动态树分治整体上由点分治发展而来。

  点分治是统计树上路径,而动态树分治用来统计与点有关的树上路径,比如多次询问某一些点到询问点的距离和。

  前置知识就是点分治。

做法

  众所周知,点分树(点分治中重心组成的树)的深度是$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】 动态树分治相关推荐

  1. P4719 【模板】“动态 DP“动态树分治(矩阵/轻重链剖分/ddp)

    P4719 [模板]"动态 DP"&动态树分治 求解树上最大权独立集,但是需要支持修改. https://www.luogu.com.cn/problem/solution ...

  2. [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA]

    [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA] 题目大意: 给定一颗nn个节点的树,边权均为11,初始每个点权值为00 . 其中操作QQ xx询问x点的点权,操作 MM xx dd ...

  3. 【BZOJ4372】烁烁的游戏 动态树分治+线段树

    [BZOJ4372]烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围 ...

  4. 【ZJOI2007】捉迷藏(动态树分治)

    显然只有一次询问的话,可以用点分治来实现. 但是现在我们有多组询问,还带有修改,我们只能通过动态点分治来做了. 动态点分治的主要思想:省去每次点分治求重心的过程,直接预处理出来(因为树的形态不会改变) ...

  5. BZOJ 3730: 震波 动态树分治 线段树 lca

    3730: 震波 Time Limit: 15 Sec  Memory Limit: 256 MB Submit: 1202  Solved: 288 [Submit][Status][Discuss ...

  6. BZOJ 4012: [HNOI2015]开店 -- 动态树分治

    4012: [HNOI2015]开店 Time Limit: 70 Sec  Memory Limit: 512 MB Submit: 1463  Solved: 635 [Submit][Statu ...

  7. 【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治

    [BZOJ3924][Zjoi2015]幻想乡战略游戏 Description 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网 ...

  8. 【bzoj4372】 烁烁的游戏【动态树分治】

    烁烁的游戏 Description 背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠. 题意: 给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠. 烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节 ...

  9. BZOJ 3435: [Wc2014]紫荆花之恋 【(替罪羊树式)动态点分治 + Treap】

    BZOJ 传送门 洛谷传送门 题目分析: 似乎做过几道点分树的题之后题解还是比较好懂的. 这位dalao的题目分析非常的到位,Orz. PoPoQQQ的具体解读也非常的清晰,Orz. R i + R ...

  10. 点分治动态点分治小结

    (写篇博客证明自己还活着×2) 转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8006488.html 有的时候,我们会发现这样一类题:它长得很像一个$O(n) ...

最新文章

  1. JavaScript 渐变效果
  2. HTTP 协议(详解)
  3. 2021下午茶数字经济蓝皮书
  4. iOS高德地图SDK定位和搜索附近信息的具体使用
  5. 点餐系统ip地址_spring boot实战——微信点餐系统03:微信授权(用户授权),免费内网穿透(固定ip)...
  6. 这些将在新一年改变你的风控内容
  7. C# winForm utf8 gbk 相互转码小工具
  8. 电商购物核心架构演进:谁说架构思路会过时?
  9. 支持SMTP邮箱介绍
  10. 《深入理解Windows操作系统》笔记5
  11. mongodb查询某个字段数据
  12. 第5关:类与对象练习------Java面向对象 - 类与对象
  13. Qt 绘制南丁格尔玫瑰图
  14. Java 判断对象是否所有属性为空
  15. 2018年苹果年费支付失败真正的原因
  16. css3怎么做多边形,CSS | 实现有趣的多边形
  17. mysql按半小时分组
  18. old DIB in res XXX ico pass it through SDKPAINT 错误
  19. Linux内核是什么?Linux内核是怎么工作的?
  20. 【codeforces 718E】E. Matvey's Birthday

热门文章

  1. 如何在Mac上访问 USB 驱动器?
  2. inDesign教程,如何制作个性化的感谢卡?
  3. indesign自学教程,如何保存文档?
  4. 专业RAW图像处理软件Capture One Pro 22
  5. PHP的新手语法介绍
  6. 如何通过 Apple Watch 解锁 Mac
  7. UCloud可支撑单可用区320,000服务器的数据中心网络系统设计
  8. 惊!这些居然不是零食而是药,你知道吗?
  9. idea 添加 golang 项目的 gopath
  10. 解决ajax中文乱码问题