树链剖分(合集)

来源:互联网 发布:爱卡自动发卡平台源码 编辑:程序博客网 时间:2024/06/12 18:00

参考链接 点击打开链接

树链剖分通常用于处理树的形态不变 但点权/边权需要修改查询的题目


树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护。

通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中size[v]是以v为根的子树的节点个数,全部由重边组成的路径是重路径,


性质:任意一点到根的路径上存在不超过logn条轻边和logn条重路径.

dfs时优先访问重儿子 得到dfs序.此时每条重链都是是某段区间,某个重链上的点都一一映射到该区间上.

由剖分的性质,两点间的路径上,不会超过logn条重链.则用线段树来维护这些重链.单次查询复杂度即可达到O(log^2n).


例题1:BZOJ 1036

题意:n个结点的树,每个结点有权值wi,Q次询问.n<=3e4,q<=2e5.
op1:改变结点u权值.op2:询问u-v路径中权值最大的是多少.op3:询问u-v路径权值和.

修改u,根据u的dfs序编号直接修改即可.
查询:当x-y处于不同的链中时,每次跳到链顶深度较大那一个.直到x-y处于同一条链为止.

#include <bits/stdc++.h>using namespace std;const int N=5e5+20;const int M=6e5+20;const int inf=0x3f3f3f3f;int n,cnt,sz;int v[N],dep[N],size[N],head[N],fa[N];int pos[N],bl[N];struct edge{int to,next;}e[M];struct node{int l,r,mx,sum;}t[N];void add_edge(int u,int v){e[++cnt].to=v,e[cnt].next=head[u],head[u]=cnt;}void init(){memset(head,-1,sizeof(head));scanf("%d",&n);int x,y;for(int i=1;i<=n-1;i++){scanf("%d%d",&x,&y);add_edge(x,y),add_edge(y,x);}for(int i=1;i<=n;i++)scanf("%d",&v[i]);}void dfs(int u){size[u]=1;for(int i=head[u];i!=-1;i=e[i].next){int v=e[i].to;if(v==fa[u])continue;dep[v]=dep[u]+1,fa[v]=u;dfs(v);size[u]+=size[v];}}void dfs(int x,int chain){int k=0;sz++;pos[x]=sz;//timebl[x]=chain;for(int i=head[x];i!=-1;i=e[i].next)//find heavy son{int v=e[i].to;if(dep[v]>dep[x]&&size[v]>size[k])k=v;}if(k==0)return;dfs(k,chain);for(int i=head[x];i!=-1;i=e[i].next)if(dep[e[i].to]>dep[x]&&k!=e[i].to)dfs(e[i].to,e[i].to);//ÆäÓà¶ù×ÓÖØ¿ªÐÂÁ´. }///segment tree.void build(int o,int l,int r){t[o].l=l,t[o].r=r;if(l==r)return;int m=l+r>>1;build(o<<1,l,m),build(o<<1|1,m+1,r);}void push_up(int o){t[o].mx=max(t[o<<1].mx,t[o<<1|1].mx),t[o].sum=t[o<<1].sum+t[o<<1|1].sum;}void change(int o,int x,int y){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l==r){t[o].sum=t[o].mx=y;return;}if(x<=mid)change(o<<1,x,y);elsechange(o<<1|1,x,y);push_up(o);}int querysum(int o,int ql,int qr){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l>=ql&&r<=qr)return t[o].sum;int res=0;if(ql<=mid)res+=querysum(o<<1,ql,qr);if(qr>mid)res+=querysum(o<<1|1,ql,qr);return res;}int querymx(int o,int ql,int qr){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l>=ql&&r<=qr)return t[o].mx;int res=-inf;if(ql<=mid)res=max(res,querymx(o<<1,ql,qr));if(qr>mid)res=max(res,querymx(o<<1|1,ql,qr));return res;}//int solvesum(int x,int y){int sum=0;while(bl[x]!=bl[y])//x,y´¦ÔÚ²»Í¬µÄÁ´ÖÐ. {if(dep[bl[x]]<dep[bl[y]]) swap(x,y);sum+=querysum(1,pos[bl[x]],pos[x]);x=fa[bl[x]];}if(pos[x]>pos[y])swap(x,y);sum+=querysum(1,pos[x],pos[y]);return sum;}int solvemx(int x,int y){int mx=-inf;while(bl[x]!=bl[y]){if(dep[bl[x]]<dep[bl[y]]) swap(x,y);mx=max(mx,querymx(1,pos[bl[x]],pos[x]));x=fa[bl[x]];}if(pos[x]>pos[y]) swap(x,y);mx=max(mx,querymx(1,pos[x],pos[y]));return mx;}void solve(){build(1,1,n);for(int i=1;i<=n;i++)change(1,pos[i],v[i]);//dfs-numint x,y,Q;char ch[10];scanf("%d",&Q);while(Q--){scanf("%s%d%d",ch,&x,&y);if(ch[0]=='C'){v[x]=y,change(1,pos[x],v[x]);}else{if(ch[1]=='M')printf("%d\n",solvemx(x,y));elseprintf("%d\n",solvesum(x,y));}}}int main(){init();dfs(1);dfs(1,1);solve();return 0;}

SPOJ QTREE

题意:n个结点的树,每个条边有权值wi,Q次询问.n<=3e4,q<=2e5.

op1:改变第i条边权值.op2:询问u-v路径中边权值最大的是多少


dfs序映射的是点的编号.现在修改的是边的权值.

因为是树只有n-1条边,将边(x,fa[x])用点x代替就好了.(每条边一一映射到每个点上).

ps:注意最后查询到同一条链时,此时最低点x维护的边不在路径上,需要将x跳到它的重儿子上.

#include <bits/stdc++.h>using namespace std;const int N=5e5+20;const int M=6e5+20;const int inf=0x3f3f3f3f;int n,cnt,sz;int w[N],dep[N],size[N],head[N],fa[N];int pos[N],bl[N],son[N];struct edge{int to,next,w;}e[M];struct Edge{int u,v,w;}a[M];struct node{int l,r,mx;}t[M];void add_edge(int u,int v,int w){e[++cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;}void init(){sz=cnt=0;memset(head,-1,sizeof(head));scanf("%d",&n);int x,y,w;for(int i=1;i<=n-1;i++){scanf("%d%d%d",&x,&y,&w);add_edge(x,y,w),add_edge(y,x,w);a[i].u=x,a[i].v=y,a[i].w=w;}}void dfs(int u){size[u]=1;for(int i=head[u];i!=-1;i=e[i].next){int v=e[i].to;if(v==fa[u])continue;w[v]=e[i].w;//w[v]:v-fa[v]'scost dep[v]=dep[u]+1,fa[v]=u;dfs(v);size[u]+=size[v];}}void dfs(int x,int chain){int k=0;sz++;pos[x]=sz;//timebl[x]=chain;for(int i=head[x];i!=-1;i=e[i].next)//find heavy son{int v=e[i].to;if(dep[v]>dep[x]&&size[v]>size[k])k=v;}son[x]=k;if(k==0){son[x]=x;return;}dfs(k,chain);for(int i=head[x];i!=-1;i=e[i].next)if(dep[e[i].to]>dep[x]&&k!=e[i].to)dfs(e[i].to,e[i].to);//ÆäÓà¶ù×ÓÖØ¿ªÐÂÁ´. }///segment tree.void build(int o,int l,int r){t[o].l=l,t[o].r=r;if(l==r)return;int m=l+r>>1;build(o<<1,l,m),build(o<<1|1,m+1,r);}void push_up(int o){t[o].mx=max(t[o<<1].mx,t[o<<1|1].mx);}void change(int o,int x,int y){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l==r){t[o].mx=y;return;}if(x<=mid)change(o<<1,x,y);elsechange(o<<1|1,x,y);push_up(o);}int querymx(int o,int ql,int qr){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l>=ql&&r<=qr)return t[o].mx;int res=-inf;if(ql<=mid)res=max(res,querymx(o<<1,ql,qr));if(qr>mid)res=max(res,querymx(o<<1|1,ql,qr));return res;}//int solvemx(int x,int y){int mx=-inf;while(bl[x]!=bl[y]){if(dep[bl[x]]<dep[bl[y]]) swap(x,y);mx=max(mx,querymx(1,pos[bl[x]],pos[x]));x=fa[bl[x]];}if(pos[x]>pos[y]) swap(x,y);mx=max(mx,querymx(1,pos[son[x]],pos[y]));//return mx;}void solve(){build(1,1,n);w[1]=-inf;for(int i=1;i<=n;i++)change(1,pos[i],w[i]);//dfsÐò ͬһÌõÁ´ÔÚ±àºÅÁ¬ÐøµÄÇø¼ä. int x,y,Q;char ch[100];while(~scanf("%s",ch)&&ch[0]!='D'){scanf("%d%d",&x,&y);if(ch[0]=='C'){int u=a[x].u,v=a[x].v;int son=fa[v]==u?v:u;change(1,pos[son],y);}elseprintf("%d\n",solvemx(x,y));}}int main(){int T;scanf("%d",&T);while(T--){init();dfs(1);dfs(1,1);solve();}return 0;}

POJ 3237

修改边值,
查询x-y路径边的最大值.
将x-y路径上的边变号.
第3个操作用线段树区间修改维护一下就好了,其余部分是一样的.

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int N=5e5+20;const int M=6e5+20;const int inf=0x3f3f3f3f;int n,cnt,sz;int w[N],dep[N],size[N],head[N],fa[N];int pos[N],bl[N],son[N];struct edge{int to,next,w;}e[M];struct Edge{int u,v,w;}a[M];struct node{int l,r,mx,mn,lz;}t[M];void add_edge(int u,int v,int w){e[++cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt;}void init(){sz=cnt=0;memset(head,-1,sizeof(head));scanf("%d",&n);int x,y,w;for(int i=1;i<=n-1;i++){scanf("%d%d%d",&x,&y,&w);add_edge(x,y,w),add_edge(y,x,w);a[i].u=x,a[i].v=y,a[i].w=w;}}void dfs(int u){size[u]=1;for(int i=head[u];i!=-1;i=e[i].next){int v=e[i].to;if(v==fa[u])continue;w[v]=e[i].w;//w[v]:v-fa[v]'scost dep[v]=dep[u]+1,fa[v]=u;dfs(v);size[u]+=size[v];}}void dfs(int x,int chain){int k=0;sz++;pos[x]=sz;//timebl[x]=chain;for(int i=head[x];i!=-1;i=e[i].next)//find heavy son{int v=e[i].to;if(dep[v]>dep[x]&&size[v]>size[k])k=v;}son[x]=k;if(k==0){son[x]=x;return;}dfs(k,chain);for(int i=head[x];i!=-1;i=e[i].next)if(dep[e[i].to]>dep[x]&&k!=e[i].to)dfs(e[i].to,e[i].to);//ÆäÓà¶ù×ÓÖØ¿ªÐÂÁ´. }///segment tree.void build(int o,int l,int r){t[o].lz=1;t[o].l=l,t[o].r=r;if(l==r)return;int m=l+r>>1;build(o<<1,l,m),build(o<<1|1,m+1,r);}void rev(int o){int tmp=t[o].mx;t[o].mx=-t[o].mn;t[o].mn=-tmp;}void push_up(int o){t[o].mx=max(t[o<<1].mx,t[o<<1|1].mx),t[o].mn=min(t[o<<1].mn,t[o<<1|1].mn);}void push_down(int o){if(t[o].lz%2){t[o<<1].lz++,t[o<<1|1].lz++;rev(o<<1),rev(o<<1|1);}t[o].lz=0;}void change(int o,int x,int y){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l==r){t[o].mx=t[o].mn=y;return;}push_down(o);if(x<=mid)change(o<<1,x,y);elsechange(o<<1|1,x,y);push_up(o);}void update(int o,int ql,int qr){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l>=ql&&r<=qr){t[o].lz++;rev(o);return;}push_down(o);if(ql<=mid)update(o<<1,ql,qr);if(qr>mid)update(o<<1|1,ql,qr);push_up(o);}int querymx(int o,int ql,int qr){int l=t[o].l,r=t[o].r,mid=l+r>>1;if(l>=ql&&r<=qr)return t[o].mx;push_down(o);int res=-inf;if(ql<=mid)res=max(res,querymx(o<<1,ql,qr));if(qr>mid)res=max(res,querymx(o<<1|1,ql,qr));return res;}//int solvemx(int x,int y){int mx=-inf;while(bl[x]!=bl[y]){if(dep[bl[x]]<dep[bl[y]]) swap(x,y);mx=max(mx,querymx(1,pos[bl[x]],pos[x]));x=fa[bl[x]];}if(pos[x]>pos[y]) swap(x,y);mx=max(mx,querymx(1,pos[son[x]],pos[y]));//return mx;}void Negate(int x,int y){while(bl[x]!=bl[y]){if(dep[bl[x]]<dep[bl[y]]) swap(x,y);update(1,pos[bl[x]],pos[x]);x=fa[bl[x]];}if(pos[x]>pos[y])swap(x,y);update(1,pos[son[x]],pos[y]);}void solve(){build(1,1,n);w[1]=-inf;for(int i=1;i<=n;i++)change(1,pos[i],w[i]);//dfsÐò ͬһÌõÁ´ÔÚ±àºÅÁ¬ÐøµÄÇø¼ä. int x,y,Q;char ch[100];while(~scanf("%s",ch)&&ch[0]!='D'){scanf("%d%d",&x,&y);if(ch[0]=='Q')printf("%d\n",solvemx(x,y));else{int u=a[x].u,v=a[x].v;int son=fa[v]==u?v:u;if(ch[0]=='C')change(1,pos[son],y);elseNegate(x,y);}}}int main(){int T;scanf("%d",&T);while(T--){init();dfs(1);dfs(1,1);solve();}return 0;}


原创粉丝点击