为什么需要树链刨分?
刚入门的同学可以先做做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 MBSubmit: 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
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
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 }
bzoj 4034
4034: [HAOI2015]树上操作
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 6005 Solved: 1946
[Submit][Status][Discuss]
Description
Input
Output
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
Sample Input
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
9
13
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 }
bzoj 3589
3589: 动态树
Time Limit: 30 Sec Memory Limit: 1024 MBSubmit: 588 Solved: 213
[Submit][Status][Discuss]
Description
Input
Output
对于每个事件1, 输出询问的果子数.
Sample Input
1 2
2 3
2 4
1 5
3
0 1 1
0 2 3
1 2 3 1 1 4
Sample Output
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
我还是老老实实地用容斥又做了一遍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 }
bzoj 2243
2243: [SDOI2011]染色
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 8748 Solved: 3285
[Submit][Status][Discuss]
Description
Input
Output
对于每个询问操作,输出一行答案。
Sample Input
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
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 }
bzoj 3531
3531: [Sdoi2014]旅行
Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 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
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
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 }
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输出一个整数,代表最好影响数值,每个输出用一个空格隔开。
样例输入
样例输出
提示
n,m<=100000