专题训练之树链剖分

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 }
HDOJ3966(带注释)
技术分享图片
  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 }
HDOJ3966(不带注释)

 

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 }
POJ2763

 

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 }
POJ3237

 




以上是关于专题训练之树链剖分的主要内容,如果未能解决你的问题,请参考以下文章

树链剖分总结线段树维护

周报八

bzoj4034 树上操作 树链剖分+线段树

树的统计Count---树链剖分

BZOJ 2243--染色(树链剖分)

树链剖分详解