树链刨分学习笔记

为什么需要树链刨分?

刚入门的同学可以先做做hdu 5692,题解链接:http://www.cnblogs.com/wujiechao/p/6725626.html

上面是一道典型的dfs时间戳+线段树题。这时候我们想,如果在树上做某条路径上节点的修改,路径节点求和或求最大这样的操作,如果操作的所有结点能在连续的一段区间内,那么我们就能直接用线段树做一个区间的操作解决了,这该多好~这可是log2n的一个操作呢。

但事实是无情的,这样的结构是很难(或者不可能?)实现的。我们就想一个折中的方式,使得平均起来我们要询问的连续区间数量最小。这样路径修改,路径求和这样的操作,我们就能在一个相对小(玄学?)时间内完成。这也是树链刨分的精华所在。也就是所谓的启发式刨分。所谓启发式也就是找一个方案使得这种做法在同类做法里面时间或空间属于较优的。

因此树链刨分是一种通过改变dfs序,使得能让你做树上线段树时,时间较少的一种解决方案。

 树链刨分结构

然后就是树链刨分到底是个什么东西呢?

可参考:蒋一瑶神的树链刨分ppt:《树链刨分及其应用》

我们改变dfs序,让dfs时间戳做出改变。当我们在某个节点p要访问子节点的时候,我们总是先访问子树节点总数最多的一个孩子节点,然后在访问其他的。这样做dfs序的改变,把整棵树分成了一堆重链和轻链(这个后面会解释)。

节点p和他子树节点总数最多的一个孩子节点之间的这条边我们称为重边,那么他的孩子节点和他孩子节点的子树节点总数最多的一个孩子节点之间也会形成一条重边,这样不断往下dfs,这些连在一起的重边就形成了一条重链。你可以发现,每条重链在改变顺序的时间戳上是连续的。

除了子树节点总数最多的那个孩子节点之间的重边外,其他边全是轻边。这个也可以链成一条很长很长的链,这样的链称为轻链,但这样的链没有什么价值,我们查询也不会查询这样的链的区间。因为这些对应的点(如果是边权修改类的那就是两点之间的边)在时间戳上并不都是连续的。

因此我们将树分成了一堆有用的重链,每次操作的区间就是相应部分的重链对应的连续区间。

瑶神的图:

 

那我们的重链就对应我上文说的查询的连续区间,这样的区间数是相对较小的。设size[p]为p的子树大小,son[p]为p的重链上的儿子(重儿子),也就是子树节点最多的儿子。那么对于除了son[p],其他节点k的size[k]<=size[p]/2。这点显而易见。

假如操作点u,v的lca为p。那么在p分别往u和v的两条路径上,我们每经过一条重链,查找规模就缩小到原来1/2,这样查询的区间数是小于等于log2n。再加上线段树操作是log2n的,那么每次操作一个u和v之间的路径是O((log2n)2)的,效率还是蛮高的。

 

树链刨分实现

不能免俗的,我们把树链刨分除了线段树部分,也就是求取重链的部分,以及在重链上询问的部分给出。

先是数据结构定义:

 1 int fa[N],top[N],num[N],son[N],deep[N],dfstm[N],id[N]; 

fa为该节点父亲节点,top为该节点的在重链上深度最小的也就是最高的节点,我们可以称之为重链头。num为节点子树节点数量,son为重儿子,deep为深度,id为该节点对应的时间戳上的位置。dfstm[I]则为时间戳上位置为i的树上结点编号。

我们可以先一遍dfs求出fa,num,son,deep。

第二遍的时候依据第一遍的dfs求出top,按照时间戳做法求出id,dfstm。

 1 void dfs1(int u,int pre,int dep)
 2 {
 3     num[u]=1;
 4     deep[u]=dep;
 5     fa[u]=pre;
 6     int p;
 7     for(int i=head[u];i!=-1;i=edge[i].next)
 8     {
 9         p=edge[i].to;
10         if(p!=pre)
11         {
12             dfs1(p,u,dep+1);
13             num[u]+=num[p];
14             if(son[u]==-1 || num[p]>num[son[u]])
15                 son[u]=p;
16         }
17     }
18     return ;
19 }
20 void dfs2(int u,int tp)
21 {
22     top[u]=tp;
23     id[u]=++cnt;
24     dfstm[cnt]=u;
25     if(son[u]!=-1)
26         dfs2(son[u],tp);
27     int p;
28     for(int i=head[u];i!=-1;i=edge[i].next)
29     {
30         p=edge[i].to;
31         if(p!=fa[u] && p!=son[u])
32             dfs2(p,p);
33     }
34     return ;
35 }

然后就是询问操作了。当u和v属于不同重链时,每次选择u和v重链头深度较深的一个,假如是u,操作u到重链头对应的区间,然后把u变为重链头的父亲节点。直到u和v属于同一条重链时,操作u和v之间的连续区间,操作over。

给出一个最大值的例子:

 1 int querym(int u,int v)//询问树上u到v的最大值
 2 {
 3     int tpu=top[u],tpv=top[v];
 4     int ans=-0x3f3f3f3f;
 5     while(tpu!=tpv)
 6     {
 7         if(deep[tpu]<deep[tpv])
 8         {
 9             swap(tpu,tpv);
10             swap(u,v);
11         }
12         ans=max(ans,querymax(1,id[tpu],id[u]));
13         u=fa[tpu];
14         tpu=top[u];
15     }
16     if(deep[u]<deep[v]) swap(u,v);
17     ans=max(ans,querymax(1,id[v],id[u]));
18     return ans;
19 }

其中querymax是询问线段树最大值的一个线段树操作。

至此该实现的都实现了。线段树这个部分你自己写去把23333。

例题

bzoj 1036

1036: [ZJOI2008]树的统计Count

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 19281  Solved: 7860
[Submit][Status][Discuss]

Description

  一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

  输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

  对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

Sample Input

4
1 2
2 3
4 1
4 2 1 3
12
QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4

Sample Output

4
1
2
2
10
6
5
6
5
16
 

单点修改的裸题,直接上树链刨分。
  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define LL long long
  5 #define mod 1000000007
  6 using namespace std;
  7 const int N=3e4+10;
  8 
  9 struct edg
 10 {
 11     int next,to;
 12 }edge[N*2];
 13 int head[N],ecnt;
 14 void addedge(int u,int v)
 15 {
 16     edge[++ecnt]=(edg){head[u],v};
 17     head[u]=ecnt;
 18     return ;
 19 }
 20 
 21 int val[N],n,m,q,T,u,v,cnt;
 22 int fa[N],top[N],num[N],son[N],deep[N],dfstm[N],id[N];
 23 char s[20];
 24 struct Seg//带有求和和求最大值的线段树
 25 {
 26     int l,r,maxn,sum;
 27 }seg[N<<2];
 28 void init(int i,int l,int r)//初始化线段树
 29 {
 30     seg[i]=(Seg){l,r};
 31     if(l==r)
 32     {
 33         seg[i].maxn=seg[i].sum=val[dfstm[l]];
 34         return ;
 35     }
 36     int mid=(l+r)>>1;
 37     init(i<<1,l,mid);
 38     init(i<<1|1,mid+1,r);
 39     seg[i].maxn=max(seg[i<<1].maxn,seg[i<<1|1].maxn);
 40     seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
 41     return ;
 42 }
 43 void update(int i,int pos,int value)//更新pos点的权值带来sum和马鑫改变
 44 {
 45     if(seg[i].r==pos && seg[i].l==pos)
 46     {
 47         seg[i].sum=seg[i].maxn=value;
 48         return ;
 49     }
 50     int mid=(seg[i].l+seg[i].r)>>1;
 51     if(mid>=pos)
 52     {
 53         update(i<<1,pos,value);
 54     }
 55     if(mid<pos)
 56     {
 57         update(i<<1|1,pos,value);
 58     }
 59     seg[i].maxn=max(seg[i<<1].maxn,seg[i<<1|1].maxn);
 60     seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
 61     return ;
 62 }
 63 int querysum(int i,int l,int r)//询问一个线段树区间[L,R]的和
 64 {
 65     if(seg[i].l>=l &&seg[i].r<=r)
 66     {
 67         return seg[i].sum;
 68     }
 69     int ans=0;
 70     int mid=(seg[i].l+seg[i].r)>>1;
 71     if(mid>=l)
 72         ans+=querysum(i<<1,l,r);
 73     if(mid<r)
 74         ans+=querysum(i<<1|1,l,r);
 75     return ans;
 76 }
 77 int querymax(int i,int l,int r)//询问一个线段树区间[L,R]的最大值
 78 {
 79     if(seg[i].l>=l &&seg[i].r<=r)
 80     {
 81         return seg[i].maxn;
 82     }
 83     int ans=-0x3f3f3f3f;
 84     int mid=(seg[i].l+seg[i].r)>>1;
 85     if(mid>=l)
 86         ans=max(ans,querymax(i<<1,l,r));
 87     if(mid<r)
 88         ans=max(ans,querymax(i<<1|1,l,r));
 89     return ans;
 90 }
 91 int querys(int u,int v)//询问树上u到v的和
 92 {
 93     int tpu=top[u],tpv=top[v];
 94     int ans=0;
 95     while(tpu!=tpv)
 96     {
 97         if(deep[tpu]<deep[tpv])
 98         {
 99             swap(tpu,tpv);
100             swap(u,v);
101         }
102         ans+=querysum(1,id[tpu],id[u]);
103         u=fa[tpu];
104         tpu=top[u];
105     }
106     if(deep[u]<deep[v]) swap(u,v);
107     ans+=querysum(1,id[v],id[u]);
108     return ans;
109 }
110 int querym(int u,int v)//询问树上u到v的最大值
111 {
112     int tpu=top[u],tpv=top[v];
113     int ans=-0x3f3f3f3f;
114     while(tpu!=tpv)
115     {
116         if(deep[tpu]<deep[tpv])
117         {
118             swap(tpu,tpv);
119             swap(u,v);
120         }
121         ans=max(ans,querymax(1,id[tpu],id[u]));
122         u=fa[tpu];
123         tpu=top[u];
124     }
125     if(deep[u]<deep[v]) swap(u,v);
126     ans=max(ans,querymax(1,id[v],id[u]));
127     return ans;
128 }
129 void inito()
130 {
131     clr_1(head);
132     clr_1(son);
133     ecnt=0;
134     cnt=0;
135     return ;
136 }
137 void dfs1(int u,int pre,int dep)
138 {
139     num[u]=1;
140     deep[u]=dep;
141     fa[u]=pre;
142     int p;
143     for(int i=head[u];i!=-1;i=edge[i].next)
144     {
145         p=edge[i].to;
146         if(p!=pre)
147         {
148             dfs1(p,u,dep+1);
149             num[u]+=num[p];
150             if(son[u]==-1 || num[p]>num[son[u]])
151                 son[u]=p;
152         }
153     }
154     return ;
155 }
156 void dfs2(int u,int tp)
157 {
158     top[u]=tp;
159     id[u]=++cnt;
160     dfstm[cnt]=u;
161     if(son[u]!=-1)
162         dfs2(son[u],tp);
163     int p;
164     for(int i=head[u];i!=-1;i=edge[i].next)
165     {
166         p=edge[i].to;
167         if(p!=fa[u] && p!=son[u])
168             dfs2(p,p);
169     }
170     return ;
171 }
172 int main()
173 {
174     scanf("%d",&n);
175     inito();
176     for(int i=1;i<n;i++)
177     {
178         scanf("%d%d",&u,&v);
179         addedge(u,v);
180         addedge(v,u);
181     }
182     for(int i=1;i<=n;i++)
183         scanf("%d",&val[i]);
184     dfs1(1,1,1);
185     dfs2(1,1);
186     init(1,1,n);
187     scanf("%d",&q);
188     for(int i=1;i<=q;i++)
189     {
190         scanf("%s%d%d",s,&u,&v);
191         if(strcmp(s,"CHANGE")==0)
192         {
193             update(1,id[u],v);
194         }
195         if(strcmp(s,"QSUM")==0)
196         {
197             printf("%d\n",querys(u,v));
198         }
199         if(strcmp(s,"QMAX")==0)
200         {
201             printf("%d\n",querym(u,v));
202         }
203     }
204     return 0;
205 }
View Code

 

 bzoj 4034

4034: [HAOI2015]树上操作

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 6005  Solved: 1946
[Submit][Status][Discuss]

Description

有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

Input

第一行包含两个整数 N, M 。表示点数和操作数。接下来一行 N 个整数,表示树中节点的初始权值。接下来 N-1 
行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。再接下来 M 行,每行分别表示一次操作。其中
第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。
 

Output

对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

 

Sample Input

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

Sample Output

6
9
13

 
 
感觉是不是和度度熊那题很像?度度熊是把这题改简单了,所以可以用前缀和。这题还有子树修改呢~。所以这题不是求最大值了,而是真的求和。询问的话需要树链刨分来优化,而普通部分就和做dfs序差不多了~。
  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define LL long long
  5 #define mod 1000000007
  6 using namespace std;
  7 const int N=1e5+10;
  8 
  9 struct edg
 10 {
 11     int next,to;
 12 }edge[N*2];
 13 int head[N],ecnt;
 14 void addedge(int u,int v)
 15 {
 16     edge[++ecnt]=(edg){head[u],v};
 17     head[u]=ecnt;
 18     return ;
 19 }
 20 
 21 LL val[N];
 22 int n,m,q,T,u,v,cnt;
 23 int fa[N],top[N],num[N],son[N],deep[N],dfstm[N],pre[N],last[N];
 24 int op;
 25 struct Seg//带有求和和求最大值的线段树
 26 {
 27     int l,r;
 28     LL sum,tag;
 29 }seg[N<<2];
 30 void init(int i,int l,int r)//初始化线段树
 31 {
 32     seg[i]=(Seg){l,r,0};
 33     if(l==r)
 34     {
 35         seg[i].sum=val[dfstm[l]];
 36         return ;
 37     }
 38     int mid=(l+r)>>1;
 39     init(i<<1,l,mid);
 40     init(i<<1|1,mid+1,r);
 41     seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
 42     return ;
 43 }
 44 void pushdown(int i)
 45 {
 46     if(seg[i].tag!=0)
 47     {
 48         if(seg[i].l!=seg[i].r)
 49         {
 50             seg[i<<1].tag+=seg[i].tag;
 51             seg[i<<1|1].tag+=seg[i].tag;
 52             seg[i<<1].sum+=(seg[i<<1].r-seg[i<<1].l+1)*seg[i].tag;
 53             seg[i<<1|1].sum+=(seg[i<<1|1].r-seg[i<<1|1].l+1)*seg[i].tag;
 54         }
 55         seg[i].tag=0;
 56     }
 57     return ;
 58 }
 59 void update(int i,int l,int r,LL val)//更新[l,r]的权值带来sum改变
 60 {
 61     if(seg[i].l>=l && seg[i].r<=r)
 62     {
 63         seg[i].tag+=val;
 64         seg[i].sum+=(seg[i].r-seg[i].l+1)*val;
 65         return ;
 66     }
 67     pushdown(i);
 68     int mid=(seg[i].l+seg[i].r)>>1;
 69     if(mid>=l)
 70     {
 71         update(i<<1,l,r,val);
 72     }
 73     if(mid<r)
 74     {
 75         update(i<<1|1,l,r,val);
 76     }
 77     seg[i].sum=seg[i<<1].sum+seg[i<<1|1].sum;
 78     return ;
 79 }
 80 LL querysum(int i,int l,int r)//询问一个线段树区间[L,R]的和
 81 {
 82     if(seg[i].l>=l &&seg[i].r<=r)
 83     {
 84         return seg[i].sum;
 85     }
 86     pushdown(i);
 87     LL ans=0;
 88     int mid=(seg[i].l+seg[i].r)>>1;
 89     if(mid>=l)
 90         ans+=querysum(i<<1,l,r);
 91     if(mid<r)
 92         ans+=querysum(i<<1|1,l,r);
 93     return ans;
 94 }
 95 LL querys(int u,int v)//询问树上u到v的和
 96 {
 97     int tpu=top[u],tpv=top[v];
 98     LL ans=0;
 99     while(tpu!=tpv)
100     {
101         if(deep[tpu]<deep[tpv])
102         {
103             swap(tpu,tpv);
104             swap(u,v);
105         }
106         ans+=querysum(1,pre[tpu],pre[u]);
107         u=fa[tpu];
108         tpu=top[u];
109     }
110     if(deep[u]<deep[v]) swap(u,v);
111     ans+=querysum(1,pre[v],pre[u]);
112     return ans;
113 }
114 void inito()
115 {
116     clr_1(head);
117     clr_1(son);
118     ecnt=0;
119     cnt=0;
120     return ;
121 }
122 void dfs1(int u,int pre,int dep)
123 {
124     num[u]=1;
125     deep[u]=dep;
126     fa[u]=pre;
127     int p;
128     for(int i=head[u];i!=-1;i=edge[i].next)
129     {
130         p=edge[i].to;
131         if(p!=pre)
132         {
133             dfs1(p,u,dep+1);
134             num[u]+=num[p];
135             if(son[u]==-1 || num[p]>num[son[u]])
136                 son[u]=p;
137         }
138     }
139     return ;
140 }
141 void dfs2(int u,int tp)
142 {
143     top[u]=tp;
144     pre[u]=++cnt;
145     dfstm[cnt]=u;
146     if(son[u]!=-1)
147         dfs2(son[u],tp);
148     int p;
149     for(int i=head[u];i!=-1;i=edge[i].next)
150     {
151         p=edge[i].to;
152         if(p!=fa[u] && p!=son[u])
153             dfs2(p,p);
154     }
155     last[u]=cnt;
156     return ;
157 }
158 int main()
159 {
160     scanf("%d%d",&n,&q);
161     inito();
162     for(int i=1;i<=n;i++)
163         scanf("%lld",&val[i]);
164     for(int i=1;i<n;i++)
165     {
166         scanf("%d%d",&u,&v);
167         addedge(u,v);
168         addedge(v,u);
169     }
170     dfs1(1,1,1);
171     dfs2(1,1);
172     init(1,1,n);
173     for(int i=1;i<=q;i++)
174     {
175         scanf("%d",&op);
176         if(op==3)
177         {
178             scanf("%d",&u);
179             printf("%lld\n",querys(1,u));
180         }
181         if(op==2)
182         {
183             scanf("%d%d",&u,&v);
184             update(1,pre[u],last[u],(LL)v);
185         }
186         if(op==1)
187         {
188             scanf("%d%d",&u,&v);
189             update(1,pre[u],pre[u],(LL)v);
190         }
191     }
192     return 0;
193 }
View Code

 

bzoj 3589

 

3589: 动态树

Time Limit: 30 Sec  Memory Limit: 1024 MB
Submit: 588  Solved: 213
[Submit][Status][Discuss]

Description

 

别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件
事件0:
这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.
事件1:
小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.
 

Input

第一行一个整数n(1<=n<=200,000), 即节点数.
接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.
在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.
最后nQ行, 每行开头要么是0, 要么是1.
如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.
如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.

Output

对于每个事件1, 输出询问的果子数.

Sample Input

5
1 2
2 3
2 4
1 5
3
0 1 1
0 2 3
1 2 3 1 1 4

Sample Output

13

HINT

 

 1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.


生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.

 

 

这题网上标解是用容斥+树链刨分,我比较奇葩,我用的就是暴力。。
修改不多说。
查询和要k次,而且去掉重复的部分。那末我们每次我们都要查询一个树枝,这个树枝涉及若干区间。因此我们想到给每个结点增加一个cover量,表示该节点前面已经覆盖过的大小。那么每次查询到一个在查询区间内的点时,就是拿sum-cover作为答案返回,并把这个点放入pt数组中,然后把该点的sum作为新cover。对于上层结点我们有可以pushuo回带cover值。而往下搜索时pushdown那些sum=cover的点,这些点下面的点肯定是cover=sum的。然后当所有值求和以后。我们把pt数组里的点到根的链上的左右孩子及自身的cover清零。
这样14s可过2333,空间开的也不大。

 

  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define LL long long
  5 using namespace std;
  6 const int N=2e5+10;
  7 const LL mod=2147483648;
  8 struct edg
  9 {
 10 int next,to;
 11 }edge[N*2];
 12 int head[N],ecnt;
 13 void addedge(int u,int v)
 14 {
 15 edge[++ecnt]=(edg){head[u],v};
 16 head[u]=ecnt;
 17 return ;
 18 }
 19 int n,m,q,T,u,v,cnt,k;
 20 LL p,ans;
 21 int fa[N],top[N],num[N],son[N],deep[N],dfstm[N],pre[N],last[N];
 22 vector<int> pt;
 23 int op;
 24 struct Seg//带有求和和的线段树
 25 {
 26 int l,r;
 27 LL sum,tag;
 28 LL cover;
 29 }seg[N<<3];
 30 void init(int i,int l,int r)//初始化线段树
 31 {
 32 seg[i]=(Seg){l,r,0,0,0};
 33 if(l==r)
 34 {
 35 return ;
 36 }
 37 int mid=(l+r)>>1;
 38 init(i<<1,l,mid);
 39 init(i<<1|1,mid+1,r);
 40 return ;
 41 }
 42 void pushdown(int i)
 43 {
 44 if(seg[i].tag!=0)
 45 {
 46 if(seg[i].l!=seg[i].r)
 47 {
 48 seg[i<<1].tag=(seg[i<<1].tag+seg[i].tag)%mod;
 49 seg[i<<1|1].tag=(seg[i<<1|1].tag+seg[i].tag)%mod;
 50 seg[i<<1].sum=(seg[i<<1].sum+((seg[i<<1].r-seg[i<<1].l+1)*seg[i].tag%mod))%mod;
 51 seg[i<<1|1].sum=(seg[i<<1|1].sum+((seg[i<<1|1].r-seg[i<<1|1].l+1)*seg[i].tag%mod))%mod;
 52 }
 53 seg[i].tag=0;
 54 }
 55 return ;
 56 }
 57 void update(int i,int l,int r,LL val)//更新[l,r]的权值带来sum改变
 58 {
 59 if(seg[i].l>=l && seg[i].r<=r)
 60 {
 61 seg[i].tag=(seg[i].tag+val)%mod;
 62 seg[i].sum=(seg[i].sum+((seg[i].r-seg[i].l+1)*val%mod))%mod;
 63 return ;
 64 }
 65 pushdown(i);
 66 int mid=(seg[i].l+seg[i].r)>>1;
 67 if(mid>=l)
 68 {
 69 update(i<<1,l,r,val);
 70 }
 71 if(mid<r)
 72 {
 73 update(i<<1|1,l,r,val);
 74 }
 75 seg[i].sum=(seg[i<<1].sum+seg[i<<1|1].sum)%mod;
 76 return ;
 77 }
 78 LL querysum(int i,int l,int r)//询问一个线段树区间[L,R]的和
 79 {
 80 // cout<<seg[i].l<<" "<<seg[i].r<<" "<<seg[i].sum<<" "<<seg[i].cover<<endl;
 81 if(seg[i].l>=l && seg[i].r<=r)
 82 {
 83 LL p=(seg[i].sum-seg[i].cover+mod)%mod;
 84 seg[i].cover=seg[i].sum;
 85 pt.push_back(i);
 86 return p;
 87 }
 88 pushdown(i);
 89 if(seg[i].cover==seg[i].sum)
 90 {
 91 seg[i<<1].cover=seg[i<<1].sum;
 92 seg[i<<1|1].cover=seg[i<<1|1].sum;
 93 }
 94 LL ans=0;
 95 int mid=(seg[i].l+seg[i].r)>>1;
 96 if(mid>=l)
 97 ans=(ans+querysum(i<<1,l,r))%mod;
 98 if(mid<r)
 99 ans=(ans+querysum(i<<1|1,l,r))%mod;
100 if(seg[i].l!=seg[i].r)
101 seg[i].cover=(seg[i<<1].cover+seg[i<<1|1].cover)%mod;
102 // cout<<seg[i].l<<" "<<seg[i].r<<" "<<seg[i].sum<<" "<<seg[i].cover<<endl;
103 return ans;
104 }
105 LL querys(int u,int v)//询问树上u到v的和
106 {
107 int tpu=top[u],tpv=top[v];
108 LL ans=0;
109 // cout<<u<<" "<<v<<" ";
110 while(tpu!=tpv)
111 {
112 if(deep[tpu]<deep[tpv])
113 {
114 swap(tpu,tpv);
115 swap(u,v);
116 }
117 ans=(ans+querysum(1,pre[tpu],pre[u]))%mod;
118 u=fa[tpu];
119 tpu=top[u];
120 }
121 if(deep[u]<deep[v]) swap(u,v);
122 ans=(ans+querysum(1,pre[v],pre[u]))%mod;
123 // cout<<ans<<endl;
124 return ans;
125 }
126 void del()
127 {
128 int len=pt.size(),p;
129 for(int i=0;i<len;i++)
130 {
131 p=pt[i];
132 while(p!=0)
133 {
134 seg[p<<1].cover=0;
135 seg[p<<1|1].cover=0;
136 seg[p].cover=0;
137 p>>=1;
138 }
139 }
140 return ;
141 }
142 void inito()
143 {
144 clr_1(head);
145 clr_1(son);
146 ecnt=0;
147 cnt=0;
148 return ;
149 }
150 void dfs1(int u,int pre,int dep)
151 {
152 num[u]=1;
153 deep[u]=dep;
154 fa[u]=pre;
155 int p;
156 for(int i=head[u];i!=-1;i=edge[i].next)
157 {
158 p=edge[i].to;
159 if(p!=pre)
160 {
161 dfs1(p,u,dep+1);
162 num[u]+=num[p];
163 if(son[u]==-1 || num[p]>num[son[u]])
164 son[u]=p;
165 }
166 }
167 return ;
168 }
169 void dfs2(int u,int tp)
170 {
171 top[u]=tp;
172 pre[u]=++cnt;
173 dfstm[cnt]=u;
174 if(son[u]!=-1)
175 dfs2(son[u],tp);
176 int p;
177 for(int i=head[u];i!=-1;i=edge[i].next)
178 {
179 p=edge[i].to;
180 if(p!=fa[u] && p!=son[u])
181 dfs2(p,p);
182 }
183 last[u]=cnt;
184 return ;
185 }
186 int main()
187 {
188 scanf("%d",&n);
189 inito();
190 for(int i=1;i<n;i++)
191 {
192 scanf("%d%d",&u,&v);
193 addedge(u,v);
194 addedge(v,u);
195 }
196 dfs1(1,1,1);
197 dfs2(1,1);
198 init(1,1,n);
199 scanf("%d",&q);
200 // for(int i=1;i<=n;i++)
201 // printf("%d ",dfstm[i]);
202 // printf("\n");
203 for(int i=1;i<=q;i++)
204 {
205 scanf("%d",&op);
206 if(op==0)
207 {
208 scanf("%d%lld",&u,&p);
209 update(1,pre[u],last[u],p%mod);
210 }
211 else
212 {
213 scanf("%d",&k);
214 ans=0;
215 pt.clear();
216 for(int j=1;j<=k;j++)
217 {
218 scanf("%d%d",&u,&v);
219 ans=(ans+querys(u,v))%mod;
220 }
221 printf("%lld\n",ans);
222 del();
223 }
224 }
225 return 0;
226 }
227 
228  
View Code

 

我还是老老实实地用容斥又做了一遍2333,时间复杂度一样高。可能是因为%的原因吧,改成位运算时间下降一半。因为%的是$2^{31}$所以直接& $2^{31}-1$。那么我们只需要用二进制枚举下树枝的搭配组合,求出他们重复的部分,偶数个树枝就在答案中减掉重复部分,奇数个就加上。这样就完美去掉重复部分了。

  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define LL long long
  5 using namespace std;
  6 const int N=2e5+10;
  7 const LL mod=2147483648;
  8 struct edg
  9 {
 10     int next,to;
 11 }edge[N*2];
 12 int head[N],ecnt;
 13 void addedge(int u,int v)
 14 {
 15     edge[++ecnt]=(edg){head[u],v};
 16     head[u]=ecnt;
 17     return ;
 18 }
 19 LL p;
 20 int n,m,q,T,u,v,cnt,k;
 21 int fa[N],top[N],num[N],son[N],deep[N],dfstm[N],pre[N],last[N];
 22 LL cal[100];
 23 int ptu[10],ptv[10];
 24 int op;
 25 struct Seg//带有求和和求最大值的线段树
 26 {
 27     int l,r;
 28     LL sum,tag;
 29 }seg[N<<2];
 30 void init(int i,int l,int r)//初始化线段树
 31 {
 32     seg[i]=(Seg){l,r,0,0};
 33     if(l==r)
 34     {
 35         return ;
 36     }
 37     int mid=(l+r)>>1;
 38     init(i<<1,l,mid);
 39     init(i<<1|1,mid+1,r);
 40     return ;
 41 }
 42 void pushdown(int i)
 43 {
 44     if(seg[i].tag!=0)
 45     {
 46         if(seg[i].l!=seg[i].r)
 47         {
 48             seg[i<<1].tag=(seg[i<<1].tag+seg[i].tag)%mod;
 49             seg[i<<1|1].tag=(seg[i<<1|1].tag+seg[i].tag)%mod;
 50             seg[i<<1].sum=(seg[i<<1].sum+((seg[i<<1].r-seg[i<<1].l+1)*seg[i].tag%mod))%mod;
 51             seg[i<<1|1].sum=(seg[i<<1|1].sum+((seg[i<<1|1].r-seg[i<<1|1].l+1)*seg[i].tag%mod))%mod;
 52         }
 53         seg[i].tag=0;
 54     }
 55     return ;
 56 }
 57 void update(int i,int l,int r,LL val)//更新[l,r]的权值带来sum改变
 58 {
 59     if(seg[i].l>=l && seg[i].r<=r)
 60     {
 61         seg[i].tag=(seg[i].tag+val)%mod;
 62         seg[i].sum=(seg[i].sum+(seg[i].r-seg[i].l+1)*val%mod)%mod;
 63         return ;
 64     }
 65     pushdown(i);
 66     int mid=(seg[i].l+seg[i].r)>>1;
 67     if(mid>=l)
 68     {
 69         update(i<<1,l,r,val);
 70     }
 71     if(mid<r)
 72     {
 73         update(i<<1|1,l,r,val);
 74     }
 75     seg[i].sum=(seg[i<<1].sum+seg[i<<1|1].sum)%mod;
 76     return ;
 77 }
 78 LL querysum(int i,int l,int r)//询问一个线段树区间[L,R]的和
 79 {
 80     if(seg[i].l>=l &&seg[i].r<=r)
 81     {
 82         return seg[i].sum;
 83     }
 84     pushdown(i);
 85     LL ans=0;
 86     int mid=(seg[i].l+seg[i].r)>>1;
 87     if(mid>=l)
 88         ans=(ans+querysum(i<<1,l,r))%mod;
 89     if(mid<r)
 90         ans=(ans+querysum(i<<1|1,l,r))%mod;
 91     return ans;
 92 }
 93 LL querys(int u,int v)//询问树上u到v的和
 94 {
 95     int tpu=top[u],tpv=top[v];
 96     LL ans=0;
 97     while(tpu!=tpv)
 98     {
 99         if(deep[tpu]<deep[tpv])
100         {
101             swap(tpu,tpv);
102             swap(u,v);
103         }
104         ans=(ans+querysum(1,pre[tpu],pre[u]))%mod;
105         u=fa[tpu];
106         tpu=top[u];
107     }
108     if(deep[u]<deep[v]) swap(u,v);
109     ans=(ans+querysum(1,pre[v],pre[u]))%mod;
110     return ans;
111 }
112 void inito()
113 {
114     clr_1(head);
115     clr_1(son);
116     ecnt=0;
117     cnt=0;
118     cal[0]=-1;
119     for(int i=1;i<=100;i++)
120     {
121         if(i&1)
122         {
123             if(cal[i>>1]==1)
124                 cal[i]=-1;
125             else
126                 cal[i]=1;
127         }
128         else
129             cal[i]=cal[i>>1];
130     }
131     return ;
132 }
133 void dfs1(int u,int pre,int dep)
134 {
135     num[u]=1;
136     deep[u]=dep;
137     fa[u]=pre;
138     int p;
139     for(int i=head[u];i!=-1;i=edge[i].next)
140     {
141         p=edge[i].to;
142         if(p!=pre)
143         {
144             dfs1(p,u,dep+1);
145             num[u]+=num[p];
146             if(son[u]==-1 || num[p]>num[son[u]])
147                 son[u]=p;
148         }
149     }
150     return ;
151 }
152 void dfs2(int u,int tp)
153 {
154     top[u]=tp;
155     pre[u]=++cnt;
156     dfstm[cnt]=u;
157     if(son[u]!=-1)
158         dfs2(son[u],tp);
159     int p;
160     for(int i=head[u];i!=-1;i=edge[i].next)
161     {
162         p=edge[i].to;
163         if(p!=fa[u] && p!=son[u])
164             dfs2(p,p);
165     }
166     last[u]=cnt;
167     return ;
168 }
169 int lca(int u,int v)
170 {
171     int tpu=top[u],tpv=top[v];
172     while(tpu!=tpv)
173     {
174         if(deep[tpu]<deep[tpv])
175         {
176             swap(tpu,tpv);
177             swap(u,v);
178         }
179         u=fa[tpu];
180         tpu=top[u];
181     }
182     if(deep[u]<deep[v])
183         return u;
184     else
185         return v;
186 }
187 LL deal(int x)
188 {
189     LL res=0;
190     int u=0,v=0,i=1;
191     while(x)
192     {
193         if(x&1)
194         {
195             if(u==0)
196             {
197                 u=ptu[i];
198                 v=ptv[i];
199             }
200             else
201             {
202                 u=lca(u,ptu[i]);
203                 if(deep[u]<deep[ptv[i]]|| deep[u]<deep[v])
204                 {
205                     u=v=-1;
206                 }
207                 else
208                 {
209                     v=deep[v]>deep[ptv[i]]?v:ptv[i];
210                 }
211                 if(u==-1)
212                     return 0;
213             }
214         }
215         x>>=1;
216         i++;
217     }
218     return querys(u,v);
219 }
220 LL cale(int k)
221 {
222     int p=1<<k;
223     LL ans=0;
224     for(int i=0;i<p;i++)
225     {
226         ans=(ans+deal(i)*cal[i]%mod)%mod;
227     }
228     return (ans%mod+mod)%mod;
229 }
230 int main()
231 {
232     scanf("%d",&n);
233     inito();
234     for(int i=1;i<n;i++)
235     {
236         scanf("%d%d",&u,&v);
237         addedge(u,v);
238         addedge(v,u);
239     }
240     dfs1(1,1,1);
241     dfs2(1,1);
242     init(1,1,n);
243     scanf("%d",&q);
244     for(int i=1;i<=q;i++)
245     {
246         scanf("%d",&op);
247         if(op==0)
248         {
249             scanf("%d%lld",&u,&p);
250             update(1,pre[u],last[u],p%mod);
251         }
252         else
253         {
254             scanf("%d",&k);
255             for(int j=1;j<=k;j++)
256             {
257                 scanf("%d%d",&u,&v);
258                 if(deep[u]<deep[v])
259                     swap(u,v);
260                 ptu[j]=u,ptv[j]=v;
261             }
262             printf("%lld\n",cale(k));
263         }
264     }
265     return 0;
266 }
View Code

 

bzoj 2243

2243: [SDOI2011]染色

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 8748  Solved: 3285
[Submit][Status][Discuss]

Description

给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。

Input

第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

Output

对于每个询问操作,输出一行答案。

 

Sample Input

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

Sample Output

3
1
2

HINT

 

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。


由于线段树的区间可加性,我们这题可以用树链刨分做。

树链刨分以外的线段树,我们维护的是这个区间是否被完全覆盖的标记tag,以及这个区间的颜色数col,还有这个区间左右端点的颜色ltag和rtag。pushup的话每个节点的col等于孩子节点col相加,若左孩子的rtag==右孩子的ltag那么这个col要--。

pushdown只对tag=1的点pushdown。孩子节点都更新成跟该结点一样的情况。

  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define LL long long
  5 #define mod 1000000007
  6 using namespace std;
  7 const int N=1e5+10;
  8 struct edg
  9 {
 10     int next,to;
 11 }edge[N<<1];
 12 int head[N],ecnt;
 13 int col[N];
 14 void addedge(int u,int v)
 15 {
 16     edge[++ecnt]=(edg){head[u],v};
 17     head[u]=ecnt;
 18     return ;
 19 }
 20 struct Seg
 21 {
 22     int l,r;
 23     int tag,col;
 24     int ltag,rtag;
 25 }seg[N<<2];
 26 int num[N],son[N],dfstm[N],pre[N],last[N],fa[N],top[N],deep[N];
 27 int n,m,p,q,u,v,T,cnt,k;
 28 char op[10];
 29 void pushdown(int i)
 30 {
 31     if(seg[i].tag)
 32     {
 33         if(seg[i].l!=seg[i].r)
 34         {
 35             seg[i<<1].tag=seg[i<<1|1].tag=1;
 36             seg[i<<1].col=seg[i<<1|1].col=1;
 37             seg[i<<1].ltag=seg[i<<1].rtag=seg[i<<1|1].ltag=seg[i<<1|1].rtag=seg[i].ltag;
 38         }
 39         seg[i].tag=0;
 40     }
 41     return ;
 42 }
 43 void pushup(int i)
 44 {
 45     seg[i].col=seg[i<<1].col+seg[i<<1|1].col;
 46     if(seg[i<<1].rtag==seg[i<<1|1].ltag)
 47         seg[i].col--;
 48     seg[i].ltag=seg[i<<1].ltag;
 49     seg[i].rtag=seg[i<<1|1].rtag;
 50     return ;
 51 }
 52 void init(int i,int l,int r)
 53 {
 54     seg[i]=(Seg){l,r,0};
 55     if(l==r)
 56     {
 57         seg[i].rtag=seg[i].ltag=col[dfstm[l]];
 58         seg[i].col=1;
 59         return ;
 60     }
 61     int mid=(l+r)>>1;
 62     init(i<<1,l,mid);
 63     init(i<<1|1,mid+1,r);
 64     pushup(i);
 65     return ;
 66 }
 67 void update(int i,int l,int r,int val)
 68 {
 69     if(seg[i].l>=l && seg[i].r<=r)
 70     {
 71         seg[i].tag=1;
 72         seg[i].ltag=seg[i].rtag=val;
 73         seg[i].col=1;
 74         return ;
 75     }
 76     pushdown(i);
 77     int mid=(seg[i].l+seg[i].r)>>1;
 78     if(mid>=l)
 79     {
 80         update(i<<1,l,r,val);
 81     }
 82     if(mid<r)
 83     {
 84         update(i<<1|1,l,r,val);
 85     }
 86     pushup(i);
 87     return ;
 88 }
 89 int query(int i,int l,int r)
 90 {
 91     if(seg[i].l>=l && seg[i].r<=r)
 92     {
 93         return seg[i].col;
 94     }
 95     pushdown(i);
 96     int mid=(seg[i].l+seg[i].r)>>1;
 97     int ans=0;
 98     if(mid<l)
 99     {
100         ans=query(i<<1|1,l,r);
101     }
102     else if(mid>=r)
103     {
104         ans=query(i<<1,l,r);
105     }
106     else
107     {
108         ans=query(i<<1,l,r)+query(i<<1|1,l,r);
109         if(seg[i<<1].rtag==seg[i<<1|1].ltag)
110             ans--;
111     }
112     return ans;
113 }
114 int getstag(int i,int pos)
115 {
116     if(seg[i].r==pos)
117         return seg[i].rtag;
118     if(seg[i].l==pos)
119         return seg[i].ltag;
120     pushdown(i);
121     int mid=(seg[i].l+seg[i].r)>>1;
122     if(mid<pos)
123         return getstag(i<<1|1,pos);
124     else
125         return getstag(i<<1,pos);
126 }
127 void inito()
128 {
129     clr_1(head);
130     clr_1(son);
131     ecnt=cnt=0;
132     fa[1]=1;
133     deep[1]=1;
134     top[1]=1;
135     return ;
136 }
137 void dfs1(int u)
138 {
139     int p;
140     num[u]=1;
141     for(int i=head[u];i!=-1;i=edge[i].next)
142     {
143         p=edge[i].to;
144         if(p!=fa[u])
145         {
146             fa[p]=u;
147             deep[p]=deep[u]+1;
148             dfs1(p);
149             num[u]+=num[p];
150             if(son[u]==-1||num[son[u]]<num[p])
151                 son[u]=p;
152         }
153     }
154     return ;
155 }
156 void dfs2(int u)
157 {
158     pre[u]=++cnt;
159     dfstm[cnt]=u;
160     if(son[u]!=-1)
161     {
162         top[son[u]]=top[u];
163         dfs2(son[u]);
164     }
165     int p;
166     for(int i=head[u];i!=-1;i=edge[i].next)
167     {
168         p=edge[i].to;
169         if(p!=fa[u] && p!=son[u])
170         {
171             top[p]=p;
172             dfs2(p);
173         }
174     }
175     last[u]=cnt;
176     return ;
177 }
178 void change(int u,int v,int val)
179 {
180     int tpu=top[u],tpv=top[v];
181     while(tpu!=tpv)
182     {
183         if(deep[tpu]<deep[tpv])
184         {
185             swap(tpu,tpv);
186             swap(u,v);
187         }
188         update(1,pre[tpu],pre[u],val);
189         u=fa[tpu];
190         tpu=top[u];
191     }
192     if(deep[u]>deep[v]) swap(u,v);
193     update(1,pre[u],pre[v],val);
194     return ;
195 }
196 int getans(int u,int v)
197 {
198     int tpu=top[u],tpv=top[v];
199     int ans=0;
200     while(tpu!=tpv)
201     {
202         if(deep[tpu]<deep[tpv])
203         {
204             swap(tpu,tpv);
205             swap(u,v);
206         }
207         ans+=query(1,pre[tpu],pre[u]);
208         if(getstag(1,pre[tpu])==getstag(1,pre[fa[tpu]]))
209             ans--;
210         u=fa[tpu];
211         tpu=top[u];
212     }
213     if(deep[u]>deep[v]) swap(u,v);
214     ans+=query(1,pre[u],pre[v]);
215     return ans;
216 }
217 int main()
218 {
219     while(scanf("%d%d",&n,&m)!=EOF)
220     {
221         inito();
222         for(int i=1;i<=n;i++)
223             scanf("%d",&col[i]);
224         for(int i=1;i<n;i++)
225         {
226             scanf("%d%d",&u,&v);
227             addedge(u,v);
228             addedge(v,u);
229         }
230         dfs1(1);
231         dfs2(1);
232         init(1,1,n);
233         for(int i=1;i<=m;i++)
234         {
235             scanf("%s",op);
236             if(op[0]=='Q')
237             {
238                 scanf("%d%d",&u,&v);
239                 printf("%d\n",getans(u,v));
240             }
241             if(op[0]=='C')
242             {
243                 scanf("%d%d%d",&u,&v,&k);
244                 change(u,v,k);
245             }
246         }
247     }
248     return 0;
249 }
View Code

 

bzoj 3531

3531: [Sdoi2014]旅行

Time Limit: 40 Sec  Memory Limit: 512 MB
Submit: 2560  Solved: 1109
[Submit][Status][Discuss]

Description

 S国有N个城市,编号从1到N。城市间用N-1条双向道路连接,满足
从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。为了方便,我们用不同的正整数代表各种宗教,  S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。
    在S国的历史上常会发生以下几种事件:
”CC x c”:城市x的居民全体改信了c教;
”CW x w”:城市x的评级调整为w;
”QS x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过的城市的评级总和;
”QM x y”:一位旅行者从城市x出发,到城市y,并记下了途中留宿过
的城市的评级最大值。
    由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。    为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

Input

    输入的第一行包含整数N,Q依次表示城市数和事件数。
    接下来N行,第i+l行两个整数Wi,Ci依次表示记录开始之前,城市i的
评级和信仰。
    接下来N-1行每行两个整数x,y表示一条双向道路。
    接下来Q行,每行一个操作,格式如上所述。

Output

    对每个QS和QM事件,输出一行,表示旅行者记下的数字。

Sample Input

5 6
3 1
2 3
1 2
3 3
5 1
1 2
1 3
3 4
3 5
QS 1 5
CC 3 1
QS 1 5
CW 3 3
QS 1 5
QM 2 4

Sample Output

8
9
11
3

 

这题典型的树链刨分。我们把信仰的宗教当做染色。那么我们对每种颜色建立一棵线段树,然后对应不同颜色在不同的线段树上操作。

由于空间有限,因此我们的线段树不能开成静态的,要动态分配结点。差不多5e6个结点就行了。

 

  1 #include<bits/stdc++.h>
  2 #define clr(x) memset(x,0,sizeof(x))
  3 #define clr_1(x) memset(x,-1,sizeof(x))
  4 #define LL long long
  5 #define mod 1000000007
  6 using namespace std;
  7 const int N=2e5+10;
  8 const int M=5e6+10;
  9 struct edg
 10 {
 11     int next,to;
 12 }edge[N<<1];
 13 int head[N],ecnt;
 14 void addedge(int u,int v)
 15 {
 16     edge[++ecnt]=(edg){head[u],v};
 17     head[u]=ecnt;
 18     return ;
 19 }
 20 struct Seg
 21 {
 22     int l,r;
 23     int sum,maxn;
 24     int lt,rt;
 25 }seg[M];
 26 int num[N],son[N],dfstm[N],pre[N],last[N],fa[N],top[N],deep[N],root[N];
 27 int n,m,p,q,T,cnt,k,u,scnt;
 28 int v;
 29 int c[N];
 30 int w[N];
 31 char op[10];
 32 int makenode(int l,int r)
 33 {
 34     seg[++scnt]=(Seg){l,r,0,0,0,0};
 35     return scnt;
 36 }
 37 void pushdown(int i)
 38 {
 39     if(seg[i].lt==0)
 40         seg[i].lt=makenode(seg[i].l,(seg[i].l+seg[i].r)>>1);
 41     if(seg[i].rt==0)
 42         seg[i].rt=makenode(((seg[i].l+seg[i].r)>>1)+1,seg[i].r);
 43     return ;
 44 }
 45 void pushup(int i)
 46 {
 47     seg[i].maxn=max(seg[seg[i].lt].maxn,seg[seg[i].rt].maxn);
 48     seg[i].sum=seg[seg[i].lt].sum+seg[seg[i].rt].sum;
 49     return ;
 50 }
 51 void update(int i,int pos,int val)
 52 {
 53     if(seg[i].l==pos && seg[i].r==pos)
 54     {
 55         seg[i].maxn=seg[i].sum=val;
 56         return ;
 57     }
 58     pushdown(i);
 59     int mid=(seg[i].l+seg[i].r)>>1;
 60     if(pos<=mid)
 61         update(seg[i].lt,pos,val);
 62     else
 63         update(seg[i].rt,pos,val);
 64     pushup(i);
 65     return ;
 66 }
 67 int querysum(int i,int l,int r)
 68 {
 69     if(!i)
 70         return 0;
 71     if(seg[i].l>=l && seg[i].r<=r)
 72     {
 73         return seg[i].sum;
 74     }
 75     int mid=(seg[i].l+seg[i].r)>>1;
 76     int ans=0;
 77     if(mid>=l)
 78         ans+=querysum(seg[i].lt,l,r);
 79     if(mid<r)
 80         ans+=querysum(seg[i].rt,l,r);
 81     return ans;
 82 }
 83 int querymaxn(int i,int l,int r)
 84 {
 85     if(!i)
 86         return 0;
 87     if(seg[i].l>=l && seg[i].r<=r)
 88     {
 89         return seg[i].maxn;
 90     }
 91     int mid=(seg[i].l+seg[i].r)>>1;
 92     int ans=0;
 93     if(mid>=l)
 94         ans=max(ans,querymaxn(seg[i].lt,l,r));
 95     if(mid<r)
 96         ans=max(ans,querymaxn(seg[i].rt,l,r));
 97     return ans;
 98 }
 99 int getmaxn(int u,int v)
100 {
101     int tpu=top[u],tpv=top[v];
102     int rt=root[c[v]];
103     int ans=0;
104     while(tpu!=tpv)
105     {
106         if(deep[tpu]<deep[tpv])
107         {
108             swap(u,v);
109             swap(tpu,tpv);
110         }
111         ans=max(ans,querymaxn(rt,pre[tpu],pre[u]));
112         u=fa[tpu];
113         tpu=top[u];
114     }
115     if(deep[u]>deep[v]) swap(u,v);
116     ans=max(ans,querymaxn(rt,pre[u],pre[v]));
117     return ans;
118 }
119 int getsum(int u,int v)
120 {
121     int tpu=top[u],tpv=top[v];
122     int rt=root[c[v]];
123     int ans=0;
124     while(tpu!=tpv)
125     {
126         if(deep[tpu]<deep[tpv])
127         {
128             swap(u,v);
129             swap(tpu,tpv);
130         }
131         ans+=querysum(rt,pre[tpu],pre[u]);
132         u=fa[tpu];
133         tpu=top[u];
134     }
135     if(deep[u]>deep[v]) swap(u,v);
136     ans+=querysum(rt,pre[u],pre[v]);
137     return ans;
138 }
139 void inito()
140 {
141     clr_1(head);
142     clr_1(son);
143     clr(root);
144     ecnt=cnt=scnt=0;
145     fa[1]=1;
146     deep[1]=1;
147     top[1]=1;
148     seg[0]=(Seg){1,n,0,0,0,0};
149     return ;
150 }
151 void dfs1(int u)
152 {
153     int p;
154     num[u]=1;
155     for(int i=head[u];i!=-1;i=edge[i].next)
156     {
157         p=edge[i].to;
158         if(p!=fa[u])
159         {
160             fa[p]=u;
161             deep[p]=deep[u]+1;
162             dfs1(p);
163             num[u]+=num[p];
164             if(son[u]==-1||num[son[u]]<num[p])
165                 son[u]=p;
166         }
167     }
168     return ;
169 }
170 void dfs2(int u)
171 {
172     pre[u]=++cnt;
173     dfstm[cnt]=u;
174     if(son[u]!=-1)
175     {
176         top[son[u]]=top[u];
177         dfs2(son[u]);
178     }
179     int p;
180     for(int i=head[u];i!=-1;i=edge[i].next)
181     {
182         p=edge[i].to;
183         if(p!=fa[u] && p!=son[u])
184         {
185             top[p]=p;
186             dfs2(p);
187         }
188     }
189     last[u]=cnt;
190     return ;
191 }
192 int main()
193 {
194     scanf("%d%d",&n,&q);
195     inito();
196     for(int i=1;i<=n;i++)
197         scanf("%d%d",&w[i],&c[i]);
198     for(int i=1;i<n;i++)
199     {
200         scanf("%d%d",&u,&v);
201         addedge(u,v);
202         addedge(v,u);
203     }
204     dfs1(1);
205     dfs2(1);
206     for(int i=1;i<=n;i++)
207     {
208         update(root[c[i]]?root[c[i]]:(root[c[i]]=makenode(1,n)),pre[i],w[i]);
209     }
210     for(int i=1;i<=q;i++)
211     {
212         scanf("%s%d%d",op,&u,&v);
213         if(op[0]=='C')
214             if(op[1]=='C')
215             {
216                 update(root[c[u]],pre[u],0);
217                 c[u]=v;
218                 update(root[c[u]]?root[c[u]]:(root[c[u]]=makenode(1,n)),pre[u],w[u]);
219             }
220             else
221             {
222                 w[u]=v;
223                 update(root[c[u]],pre[u],w[u]);
224             }
225         else
226             if(op[1]=='S')
227             {
228                 printf("%d\n",getsum(u,v));
229             }
230             else
231             {
232                 printf("%d\n",getmaxn(u,v));
233             }
234     }
235     return 0;
236 }
View Code

 

hfutxc oj 1103

1103: 插线板

时间限制: 1 Sec  内存限制: 128 MB
提交: 14  解决: 7

 

题目描述

        从前有一堆古老的插线板,任意两个插线板之间只有一根导线相连,最终形成一个树状结构。我们假设电线的电阻可以忽略。由于年代久远,插线板的状况不容乐观。每个插线板都对电流有一定的影响,用一个整数表示,若为正,表示对电流起到稳定作用,越大越稳定,若为负,代表对电流产生不好的影响。插线板在连接后。这种影响会叠加,比如a和b连接,a的影响为-1,b为3,则a到b的影响为2。现在提供操作1和2,操作1表示求出插线板a到插线板b之间对电流影响最为乐观的一段的影响数值。(注:数据有问题,这一段元素可以为空)即将a到b的这条“链”取出,形成一个关于影响的序列,这个序列的最大子段和。操作2表示将a到b的链上所有插线板对电流的影响改为一个数值(包括a和b)。现给出插线板的初始状况,请按照要求进行操作,输出结果。

 

输入

 单组测试数据

第一行为插线板个数n。

第二行为插线板1到n初始状态对电流的影响数值。(数值绝对值不超过10000)

第三行开始有n-1行,每行两个数a和b,表示a、b有电线连接,保证形成一棵树。

之后一行有一个正整数m,代表操作个数。

最后m行,每行第一个数为1或2,代表操作种类,若为1,后面跟随两个数l和r,代表求出l和r之间的最好影响数值。若为2,后面跟随三个数l、r和c,代表修改l到r的插线板的影响为c。

输出

对每个操作1输出一个整数,代表最好影响数值,每个输出用一个空格隔开。

样例输入

5 -3 -2 1 2 3 1 2 2 3 1 4 4 5 3 1 2 5 2 3 4 2 1 2 5

样例输出

5 9

提示

 

 n,m<=100000

 

posted @ 2017-11-12 13:45  hk_lin  阅读(453)  评论(1编辑  收藏  举报