专题训练之树链剖分
Posted hdujackyan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专题训练之树链剖分相关的知识,希望对你有一定的参考价值。
推荐几个博客:https://blog.csdn.net/y990041769/article/details/40348013 树链剖分详解
https://blog.csdn.net/ACdreamers/article/details/10591443 树链剖分原理
1.(HDOJ3966)http://acm.hdu.edu.cn/showproblem.php?pid=3966
题意:给一棵树,并给定各个点权的值,然后有3种操作:
I C1 C2 K: 把C1与C2的路径上的所有点权值加上K
D C1 C2 K:把C1与C2的路径上的所有点权值减去K
Q C:查询节点编号为C的权值
分析:模板题(关于点权的树链剖分),先进行剖分,然后用树状数组去维护即可。区间修改,单点查询
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=50010; 6 struct Edge 7 { 8 int to,nxt; 9 }edge[maxn*2]; 10 int head[maxn],tot; 11 int top[maxn]; //top[v]表示v所在重链的顶端节点 12 int fa[maxn]; //父亲节点 13 int dep[maxn]; //深度 14 int num[maxn]; //num[v]表示以v为根的子树的节点数 15 int p[maxn]; //每个节点剖分后的新编号 16 int fp[maxn]; //当前节点在树状数组中的位置 17 int son[maxn]; //重儿子 18 int pos,n; 19 int bit[maxn]; 20 int a[maxn]; 21 22 void init() 23 { 24 tot=0; 25 memset(head,-1,sizeof(head)); 26 pos=1; //使用树状数组,编号从1开始 27 memset(son,-1,sizeof(son)); 28 } 29 30 void addedge(int u,int v) 31 { 32 edge[tot].to=v; 33 edge[tot].nxt=head[u]; 34 head[u]=tot++; 35 } 36 37 void dfs1(int u,int pre,int d) 38 { 39 dep[u]=d; 40 fa[u]=pre; 41 num[u]=1; 42 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 43 { 44 int v=edge[i].to; 45 if ( v!=pre ) 46 { 47 dfs1(v,u,d+1); 48 num[u]+=num[v]; 49 if ( son[u]==-1 || num[v]>num[son[u]] ) son[u]=v; 50 } 51 } 52 } 53 54 void dfs2(int u,int sp) 55 { 56 top[u]=sp; 57 p[u]=pos++; 58 fp[p[u]]=u; 59 if ( son[u]==-1 ) return; 60 dfs2(son[u],sp); 61 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 62 { 63 int v=edge[i].to; 64 if ( v!=son[u] && v!=fa[u] ) dfs2(v,v); 65 } 66 } 67 68 int lowbit(int x) 69 { 70 return x&(-x); 71 } 72 73 void add(int k,int val) 74 { 75 while ( k<=n ) 76 { 77 bit[k]+=val; 78 k+=lowbit(k); 79 } 80 } 81 82 int sum(int k) 83 { 84 int s=0; 85 while ( k ) 86 { 87 s+=bit[k]; 88 k-=lowbit(k); 89 } 90 return s; 91 } 92 93 void update(int u,int v,int val) //u->v的路径上点的值的改变 94 { 95 int f1=top[u],f2=top[v]; 96 int tmp=0; 97 while ( f1!=f2 ) 98 { 99 if ( dep[f1]<dep[f2] ) 100 { 101 swap(f1,f2); 102 swap(u,v); 103 } 104 add(p[f1],val); 105 add(p[u]+1,-val); 106 u=fa[f1]; 107 f1=top[u]; 108 } 109 if ( dep[u]>dep[v] ) swap(u,v); 110 add(p[u],val); 111 add(p[v]+1,-val); 112 } 113 114 int main() 115 { 116 int m,P,u,v,C1,C2,K; 117 char op[10]; 118 while ( scanf("%d%d%d",&n,&m,&P)!=EOF ) 119 { 120 init(); 121 for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]); 122 while ( m-- ) 123 { 124 scanf("%d%d",&u,&v); 125 addedge(u,v); 126 addedge(v,u); 127 } 128 dfs1(1,0,0); 129 dfs2(1,1); 130 memset(bit,0,sizeof(bit)); 131 for ( int i=1;i<=n;i++ ) 132 { 133 add(p[i],a[i]); 134 add(p[i]+1,-a[i]); 135 } 136 while ( P-- ) 137 { 138 scanf("%s",op); 139 if ( op[0]==‘Q‘ ) 140 { 141 scanf("%d",&u); 142 printf("%d ",sum(p[u])); 143 } 144 else 145 { 146 scanf("%d%d%d",&C1,&C2,&K); 147 if ( op[0]==‘D‘ ) K=-K; 148 update(C1,C2,K); 149 } 150 } 151 } 152 return 0; 153 }
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=50010; 6 struct Edge 7 { 8 int to,nxt; 9 }edge[maxn*2]; 10 int head[maxn],tot; 11 int top[maxn]; 12 int fa[maxn]; 13 int dep[maxn]; 14 int num[maxn]; 15 int p[maxn]; 16 int fp[maxn]; 17 int son[maxn]; 18 int pos,n; 19 int bit[maxn]; 20 int a[maxn]; 21 22 void init() 23 { 24 tot=0; 25 memset(head,-1,sizeof(head)); 26 pos=1; 27 memset(son,-1,sizeof(son)); 28 } 29 30 void addedge(int u,int v) 31 { 32 edge[tot].to=v; 33 edge[tot].nxt=head[u]; 34 head[u]=tot++; 35 } 36 37 void dfs1(int u,int pre,int d) 38 { 39 dep[u]=d; 40 fa[u]=pre; 41 num[u]=1; 42 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 43 { 44 int v=edge[i].to; 45 if ( v!=pre ) 46 { 47 dfs1(v,u,d+1); 48 num[u]+=num[v]; 49 if ( son[u]==-1 || num[v]>num[son[u]] ) son[u]=v; 50 } 51 } 52 } 53 54 void dfs2(int u,int sp) 55 { 56 top[u]=sp; 57 p[u]=pos++; 58 fp[p[u]]=u; 59 if ( son[u]==-1 ) return; 60 dfs2(son[u],sp); 61 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 62 { 63 int v=edge[i].to; 64 if ( v!=son[u] && v!=fa[u] ) dfs2(v,v); 65 } 66 } 67 68 int lowbit(int x) 69 { 70 return x&(-x); 71 } 72 73 void add(int k,int val) 74 { 75 while ( k<=n ) 76 { 77 bit[k]+=val; 78 k+=lowbit(k); 79 } 80 } 81 82 int sum(int k) 83 { 84 int s=0; 85 while ( k ) 86 { 87 s+=bit[k]; 88 k-=lowbit(k); 89 } 90 return s; 91 } 92 93 void update(int u,int v,int val) 94 { 95 int f1=top[u],f2=top[v]; 96 int tmp=0; 97 while ( f1!=f2 ) 98 { 99 if ( dep[f1]<dep[f2] ) 100 { 101 swap(f1,f2); 102 swap(u,v); 103 } 104 add(p[f1],val); 105 add(p[u]+1,-val); 106 u=fa[f1]; 107 f1=top[u]; 108 } 109 if ( dep[u]>dep[v] ) swap(u,v); 110 add(p[u],val); 111 add(p[v]+1,-val); 112 } 113 114 int main() 115 { 116 int m,P,u,v,C1,C2,K; 117 char op[10]; 118 while ( scanf("%d%d%d",&n,&m,&P)!=EOF ) 119 { 120 init(); 121 for ( int i=1;i<=n;i++ ) scanf("%d",&a[i]); 122 while ( m-- ) 123 { 124 scanf("%d%d",&u,&v); 125 addedge(u,v); 126 addedge(v,u); 127 } 128 dfs1(1,0,0); 129 dfs2(1,1); 130 memset(bit,0,sizeof(bit)); 131 for ( int i=1;i<=n;i++ ) 132 { 133 add(p[i],a[i]); 134 add(p[i]+1,-a[i]); 135 } 136 while ( P-- ) 137 { 138 scanf("%s",op); 139 if ( op[0]==‘Q‘ ) 140 { 141 scanf("%d",&u); 142 printf("%d ",sum(p[u])); 143 } 144 else 145 { 146 scanf("%d%d%d",&C1,&C2,&K); 147 if ( op[0]==‘D‘ ) K=-K; 148 update(C1,C2,K); 149 } 150 } 151 } 152 return 0; 153 }
2.(POJ2763)http://poj.org/problem?id=2763
题意:给一个数,边之间有权值,然后两种操作,第一种:求任意两点的权值和,第二,修改树上两点的权值。
分析:模板题(关于边权的树链剖分),用线段树维护,同时用边的孩子节点来表示该边。单点查询,区间修改
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define lson l,m,rt*2 5 #define rson m+1,r,rt*2+1 6 #define root 1,n,1 7 using namespace std; 8 const int maxn=100010; 9 struct Edge{ 10 int to,nxt; 11 }edge[maxn*2]; 12 int head[maxn],tot; 13 int top[maxn],fa[maxn],dep[maxn],num[maxn],p[maxn],fp[maxn],son[maxn]; 14 int pos,n; 15 int sum[maxn*4]; 16 int e[maxn][3]; 17 18 void pushup(int rt) 19 { 20 sum[rt]=sum[rt*2]+sum[rt*2+1]; 21 } 22 23 void build(int l,int r,int rt) 24 { 25 if ( l==r ) 26 { 27 sum[rt]=0; 28 return; 29 } 30 int m=(l+r)/2; 31 build(lson); 32 build(rson); 33 pushup(rt); 34 } 35 36 void update(int k,int val,int l,int r,int rt) 37 { 38 if ( l==r ) 39 { 40 sum[rt]=val; 41 return; 42 } 43 int m=(l+r)/2; 44 if ( k<=m ) update(k,val,lson); 45 else update(k,val,rson); 46 pushup(rt); 47 } 48 49 int query(int L,int R,int l,int r,int rt) 50 { 51 if ( L<=l && r<=R ) return sum[rt]; 52 int m=(l+r)/2; 53 int ret=0; 54 if ( L<=m ) ret+=query(L,R,lson); 55 if ( R>m ) ret+=query(L,R,rson); 56 return ret; 57 } 58 59 void init() 60 { 61 tot=0; 62 memset(head,-1,sizeof(head)); 63 pos=0; 64 memset(son,-1,sizeof(son)); 65 } 66 67 void addedge(int u,int v) 68 { 69 edge[tot].to=v; 70 edge[tot].nxt=head[u]; 71 head[u]=tot++; 72 } 73 74 void dfs1(int u,int pre,int d) 75 { 76 dep[u]=d; 77 fa[u]=pre; 78 num[u]=1; 79 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 80 { 81 int v=edge[i].to; 82 if ( v!=pre ) 83 { 84 dfs1(v,u,d+1); 85 num[u]+=num[v]; 86 if ( son[u]==-1 || num[v]>num[son[u]] ) son[u]=v; 87 } 88 } 89 } 90 91 void dfs2(int u,int sp) 92 { 93 top[u]=sp; 94 p[u]=pos++; 95 fp[p[u]]=u; 96 if ( son[u]==-1 ) return; 97 dfs2(son[u],sp); 98 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 99 { 100 int v=edge[i].to; 101 if ( v!=son[u] && v!=fa[u] ) dfs2(v,v); 102 } 103 } 104 105 int find(int u,int v) 106 { 107 int f1=top[u],f2=top[v]; 108 int tmp=0; 109 while ( f1!=f2 ) 110 { 111 if ( dep[f1]<dep[f2] ) 112 { 113 swap(f1,f2); 114 swap(u,v); 115 } 116 tmp+=query(p[f1],p[u],root); 117 u=fa[f1],f1=top[u]; 118 } 119 if ( u==v ) return tmp; 120 if ( dep[u]>dep[v] ) swap(u,v); 121 return tmp+query(p[son[u]],p[v],root); 122 } 123 124 int main() 125 { 126 int q,s,u,v,op; 127 while ( scanf("%d%d%d",&n,&q,&s)!=EOF ) 128 { 129 init(); 130 for ( int i=1;i<n;i++ ) 131 { 132 for ( int j=0;j<3;j++ ) scanf("%d",&e[i][j]); 133 addedge(e[i][0],e[i][1]); 134 addedge(e[i][1],e[i][0]); 135 } 136 dfs1(1,0,0); 137 dfs2(1,1); 138 build(root); 139 for ( int i=1;i<n;i++ ) 140 { 141 if ( dep[e[i][0]]>dep[e[i][1]] ) swap(e[i][0],e[i][1]); 142 update(p[e[i][1]],e[i][2],root); 143 } 144 while ( q-- ) 145 { 146 scanf("%d",&op); 147 if ( op==0 ) 148 { 149 scanf("%d",&v); 150 printf("%d ",find(s,v)); 151 s=v; 152 } 153 else 154 { 155 scanf("%d%d",&u,&v); 156 update(p[e[u][1]],v,root); 157 } 158 } 159 } 160 return 0; 161 }
3.(POJ3237)http://poj.org/problem?id=3237
题意:给你由N个结点组成的树。树的节点被编号为1到N,边被编号为1到N-1。每一条边有一个权值。然后你要在树上执行一系列指令。指令可以是如下三种之一:
CHANGE i v:将第i条边的权值改成v。
NEGATE a b:将点a到点b路径上所有边的权值变成其相反数。
QUERY a b:找出点a到点b路径上各边的最大权值。
分析:关于边权的树链剖分,既有单点更新又有区间更新,区间查询。对于操作2,设置变量add(lazy标记),0表示不乘-1,1表示乘-1。同时需要同时设置最大值和最小值。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define lson l,m,rt*2 5 #define rson m+1,r,rt*2+1 6 #define root 1,n,1 7 using namespace std; 8 const int maxn=100010; 9 const int inf=1e9; 10 struct Edge{ 11 int to,nxt; 12 }edge[maxn*2]; 13 struct node{ 14 int Max,Min,add; 15 }arr[maxn*4]; 16 int head[maxn],tot; 17 int top[maxn],fa[maxn],dep[maxn],num[maxn],p[maxn],fp[maxn],son[maxn]; 18 int pos,n; 19 int e[maxn][3]; 20 21 void pushup(int rt) 22 { 23 arr[rt].Max=max(arr[rt*2].Max,arr[rt*2+1].Max); 24 arr[rt].Min=min(arr[rt*2].Min,arr[rt*2+1].Min); 25 } 26 27 void pushdown(int rt,int m) 28 { 29 if ( m==0 ) return; 30 if ( arr[rt].add ) 31 { 32 arr[rt*2].Max*=-1; 33 arr[rt*2].Min*=-1; 34 arr[rt*2].add^=1; 35 arr[rt*2+1].Max*=-1; 36 arr[rt*2+1].Min*=-1; 37 arr[rt*2+1].add^=1; 38 swap(arr[rt*2].Max,arr[rt*2].Min); 39 swap(arr[rt*2+1].Max,arr[rt*2+1].Min); 40 arr[rt].add=0; 41 } 42 } 43 44 void build(int l,int r,int rt) 45 { 46 if ( l==r ) 47 { 48 arr[rt].Max=0; 49 arr[rt].Min=0; 50 arr[rt].add=0; 51 return; 52 } 53 int m=(l+r)/2; 54 build(lson); 55 build(rson); 56 pushup(rt); 57 } 58 59 void update(int k,int val,int l,int r,int rt) 60 { 61 if ( l==r ) 62 { 63 arr[rt].Max=val; 64 arr[rt].Min=val; 65 arr[rt].add=0; 66 return; 67 } 68 pushdown(rt,r-l+1); 69 int m=(l+r)/2; 70 if ( k<=m ) update(k,val,lson); 71 else update(k,val,rson); 72 pushup(rt); 73 } 74 75 void update_(int L,int R,int l,int r,int rt) 76 { 77 if ( L<=l && r<=R ) 78 { 79 arr[rt].Max*=-1; 80 arr[rt].Min*=-1; 81 arr[rt].add^=1; 82 swap(arr[rt].Max,arr[rt].Min); 83 return; 84 } 85 pushdown(rt,r-l+1); 86 int m=(l+r)/2; 87 if ( L<=m ) update_(L,R,lson); 88 if ( m<R ) update_(L,R,rson); 89 pushup(rt); 90 } 91 92 int query(int L,int R,int l,int r,int rt) 93 { 94 if ( L<=l && r<=R ) return arr[rt].Max; 95 pushdown(rt,r-l+1); 96 int m=(l+r)/2; 97 int ret=-inf; 98 if ( L<=m ) ret=max(ret,query(L,R,lson)); 99 if ( R>m ) ret=max(ret,query(L,R,rson)); 100 pushup(rt); 101 return ret; 102 } 103 104 void init() 105 { 106 tot=0; 107 memset(head,-1,sizeof(head)); 108 pos=0; 109 memset(son,-1,sizeof(son)); 110 } 111 112 void addedge(int u,int v) 113 { 114 edge[tot].to=v; 115 edge[tot].nxt=head[u]; 116 head[u]=tot++; 117 } 118 119 void dfs1(int u,int pre,int d) 120 { 121 dep[u]=d; 122 fa[u]=pre; 123 num[u]=1; 124 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 125 { 126 int v=edge[i].to; 127 if ( v!=pre ) 128 { 129 dfs1(v,u,d+1); 130 num[u]+=num[v]; 131 if ( son[u]==-1 || num[v]>num[son[u]] ) son[u]=v; 132 } 133 } 134 } 135 136 void dfs2(int u,int sp) 137 { 138 top[u]=sp; 139 p[u]=pos++; 140 fp[p[u]]=u; 141 if ( son[u]==-1 ) return; 142 dfs2(son[u],sp); 143 for ( int i=head[u];i!=-1;i=edge[i].nxt ) 144 { 145 int v=edge[i].to; 146 if ( v!=son[u] && v!=fa[u] ) dfs2(v,v); 147 } 148 } 149 150 int find(int u,int v) 151 { 152 int f1=top[u],f2=top[v]; 153 int tmp=-inf; 154 while ( f1!=f2 ) 155 { 156 if ( dep[f1]<dep[f2] ) 157 { 158 swap(f1,f2); 159 swap(u,v); 160 } 161 tmp=max(tmp,query(p[f1],p[u],root)); 162 u=fa[f1],f1=top[u]; 163 } 164 if ( u==v ) return tmp; 165 if ( dep[u]>dep[v] ) swap(u,v); 166 return max(tmp,query(p[son[u]],p[v],root)); 167 } 168 169 void find_(int u,int v) 170 { 171 int f1=top[u],f2=top[v]; 172 while ( f1!=f2 ) 173 { 174 if ( dep[f1]<dep[f2] ) 175 { 176 swap(f1,f2); 177 swap(u,v); 178 } 179 update_(p[f1],p[u],root); 180 u=fa[f1],f1=top[u]; 181 } 182 if ( u==v ) return; 183 if ( dep[u]>dep[v] ) swap(u,v); 184 update_(p[son[u]],p[v],root); 185 return; 186 } 187 188 int main() 189 { 190 int q,s,u,v,T; 191 char op[10]; 192 scanf("%d",&T); 193 while ( T-- ) 194 { 195 scanf("%d",&n); 196 init(); 197 for ( int i=1;i<n;i++ ) 198 { 199 for ( int j=0;j<3;j++ ) scanf("%d",&e[i][j]); 200 addedge(e[i][0],e[i][1]); 201 addedge(e[i][1],e[i][0]); 202 } 203 dfs1(1,0,0); 204 dfs2(1,1); 205 build(root); 206 for ( int i=1;i<n;i++ ) 207 { 208 if ( dep[e[i][0]]>dep[e[i][1]] ) swap(e[i][0],e[i][1]); 209 update(p[e[i][1]],e[i][2],root); 210 } 211 while ( scanf("%s",op)==1 ) 212 { 213 if ( op[0]==‘D‘ ) break; 214 scanf("%d%d",&u,&v); 215 if ( op[0]==‘Q‘ ) printf("%d ",find(u,v)); 216 else if ( op[0]==‘C‘ ) update(p[e[u][1]],v,root); 217 else find_(u,v); 218 } 219 } 220 return 0; 221 }
以上是关于专题训练之树链剖分的主要内容,如果未能解决你的问题,请参考以下文章