这道题好干燥啊。。。折腾了半个月。。。感谢bogo大佬对我的指导。。。
题目要求支持的操作:1.查询某段路径的所有子路径的xor值之和;2.修改某条边的权值。重点是操作1。
刚开始,我看到了操作1之后就不自觉的想到了非~常暴力的东西。。。还好大佬及时把我引上正途:分治!
大家知道,最大子段和有个分治算法,本题的方法就跟这个比较类似。
对于一段子路径,若它能对答案产生贡献,那么它要么完全在左儿子中,要么完全在右儿子中,要么跨越左右儿子。
对于每段路径,我们需要记录如下变量:yh:异或和,ans:答案,就是要查询的东西,p0[i]:此序列的前缀序列中,异或和的二进制第i位为0的序列有多少段,后面的p1,s0,s1类似。
于是,在分治的合并阶段,答案便分为两个部分:第一部分是左右儿子返回的ans;第二部分是左儿子的s0[i]*p1[i]和s1[i]*p0[i],这两个结果还要再乘以(1 << i),表示有多少段跨越左右儿子的子路径的xor值的二进制第i位为1,乘上(1 << i)之后就表示答案实际应该累加的值。因为0和1、1和0异或的结果是1嘛,因此就对答案产生了贡献。
我们当然也要维护p0、p1、s0、s1。这里较上面简单一些,细节详见代码的rg_a结构体定义部分。
以上讨论的都是链上的做法,题目给定的是一棵树,那么树剖就Ok了,之后扔到线段树里大力merge即可~
对于修改操作,在线段树底层重建节点,然后顺次merge其所有祖先即可。
下面说几个疑难的问题:
一.要建线段树,要求权值在点上,但题目却说在边上。怎么办呢?可以把每条边的边权下放到树中此条边下方连接的节点上。这样,根节点就不会被下放,但是并不影响结果,至于为什么,会在下面提到。
二.查询时,对于一段路径u->lca->v,因为lca也被下放了权值,但是它对应的边并不在u->v路径中,因此不能被统计,所以查询时只统计路径上除lca之外的点。鉴于此,上面提到的根节点便无所谓是否下放了。具体操作时,在树剖“跳”的过程的末端稍加修改即可。
三.这点非常重要!
我在做这题时,前几份代码狂WA不止,后来找到原因:merge操作不满足交换律,但是我在查询时却出现了运算顺序的漏洞。经过一番脑洞,我找到了正确的运算顺序,现描述如下:
1.将树剖的待合并结果分成u->lca和lca->v两部分,存入数组TL和TR;
2.将TL和TR的结果分别全部合并到TL[1]和TR[1]中(注意这里的运算顺序,建议手动画图验证);
3.进行特判,如果TL为空,那么直接返回TR[1].ans,反之亦然;
4.若TL、TR均非空,则先将TL[1]进行“翻转”(细节见代码,同样建议画图验证),然后合并TL[1]、TR[1],返回合并后的ans即可。
四.有个小坑,或许是我不够细心吧,那就是查询的路径的起点和终点有可能是同一个点。开始没注意这个,结果导致WA成70分。所以我在查询时加了个特判,若u==v则直接返回0。
代码如下,又丑又长,见谅见谅:
1 #include<cstdio>
2 #include<iostream>
3 #include<cstring>
4 #include<cmath>
5 #include<ctime>
6 #include<cstdlib>
7
8 #include<string>
9 #include<stack>
10 #include<queue>
11 #include<vector>
12 #include<algorithm>
13 #include<map>
14 #include<set>
15
16 #define inf 2147483647
17 #define ri register int
18 #define ll long long
19
20 #define mid (l+r>>1)
21 #define lson (o<<1)
22 #define rson ((o<<1)+1)
23
24 using namespace std;
25
26 inline void read(int &x){
27 x=0;
28 char t=getchar();
29 bool f=0;
30
31 while(t<'0' || t>'9'){
32 if(t=='-')f=1;
33 t=getchar();
34 }
35
36 while(t>='0' && t<='9'){
37 x=(x<<3)+(x<<1)+t-'0';
38 t=getchar();
39 }
40
41 if(f)x=-x;
42 }
43
44 inline void addedge(int,int,int);
45 int u[60005];
46 int v[60005];
47 int w[60005];
48 int fi[30005];
49 int ne[60005];
50 int pe=0; //无向邻接表
51
52 int wp[30005]; //下放的点权
53
54 void dfs1(int);
55 int fa[30005]; //父亲
56 int dep[30005]; //深度
57 int size[30005]; //子树大小
58 int son[30005]; //重儿子
59
60 void dfs2(int);
61 int top[30005]; //链顶节点
62 int dfsx[30005]; //dfs序
63 int xu=0;
64
65 int pos[30005]; //节点位置,in Sgt.
66
67 struct rg_a{
68 ll yh; //异或和
69 ll ans; //答案
70 ll p0[10],p1[10],s0[10],s1[10]; //本段区间二进制第j位为0/1的前/后缀区间数
71
72 inline void merge(rg_a &A,rg_a &B){ //合并
73 rg_a T;
74 int bl,br;
75
76 T.yh=A.yh^B.yh;
77 T.ans=A.ans+B.ans;
78
79 for(ri i=0;i<10;i++){
80 T.ans+=A.s0[i]*B.p1[i]*(1<<i);
81 T.ans+=A.s1[i]*B.p0[i]*(1<<i);
82
83 bl=(A.yh>>i)&1;
84 br=(B.yh>>i)&1;
85
86 T.p0[i]=A.p0[i];
87 if(bl)T.p0[i]+=B.p1[i];
88 else T.p0[i]+=B.p0[i];
89
90 T.p1[i]=A.p1[i];
91 if(bl)T.p1[i]+=B.p0[i];
92 else T.p1[i]+=B.p1[i];
93
94 T.s0[i]=B.s0[i];
95 if(br)T.s0[i]+=A.s1[i];
96 else T.s0[i]+=A.s0[i];
97
98 T.s1[i]=B.s1[i];
99 if(br)T.s1[i]+=A.s0[i];
100 else T.s1[i]+=A.s1[i];
101 }
102
103 *this=T;
104 }
105 };
106
107 struct sgt{ //线段树
108 rg_a node[120005];
109
110 void build(int o,int l,int r){
111 if(l==r){
112 node[o].yh=node[o].ans=wp[dfsx[l]];
113
114 for(ri i=0;i<10;i++){
115 node[o].p0[i]=node[o].s0[i]=((node[o].yh>>i)&1)^1;
116 node[o].p1[i]=node[o].s1[i]=(node[o].yh>>i)&1;
117 }
118 }
119 else{
120 build(lson,l,mid);
121 build(rson,mid+1,r);
122
123 node[o].merge(node[lson],node[rson]);
124 }
125 }
126
127 void update(int o,int l,int r,int p,int x){
128 if(l==r && l==p){
129 node[o].yh=node[o].ans=x;
130
131 for(ri i=0;i<10;i++){
132 node[o].p0[i]=node[o].s0[i]=((x>>i)&1)^1;
133 node[o].p1[i]=node[o].s1[i]=(x>>i)&1;
134 }
135 }
136 else{
137 if(p<=mid)update(lson,l,mid,p,x);
138 else update(rson,mid+1,r,p,x);
139
140 node[o].merge(node[lson],node[rson]);
141 }
142 }
143
144 rg_a query(int o,int l,int r,int a,int b){
145 if(l>=a && r<=b)return node[o];
146 else{
147 if(b<=mid)return query(lson,l,mid,a,b);
148 else if(a>mid)return query(rson,mid+1,r,a,b);
149 else{
150 rg_a tl=query(lson,l,mid,a,b);
151 rg_a tr=query(rson,mid+1,r,a,b);
152
153 tl.merge(tl,tr);
154 return tl;
155 }
156 }
157 }
158 } tree;
159
160 inline ll path_query(int,int);
161 rg_a TL[50],TR[50]; //外层查询临时结果
162 int pl,pr; //记录TL和TR存放的临时结果的数量
163
164 int n,q;
165 int f,x,y,z;
166 int root;
167
168 int main(){
169 srand(time(0)+19260817);
170
171 read(n);read(q);
172 root=rand()%n+1; //Ha~
173
174 for(ri i=1;i<n;i++){
175 read(x);read(y);read(z);
176 addedge(x,y,z);
177 addedge(y,x,z);
178 }
179
180 fa[root]=0;
181 dep[root]=1;
182 wp[root]=0;
183 dfs1(root);
184
185 top[root]=root;
186 dfs2(root);
187
188 for(ri i=1;i<=n;i++)pos[dfsx[i]]=i;
189
190 tree.build(1,1,n);
191
192 while(q--){
193 read(f);
194
195 if(f==1){
196 read(x);read(y);
197 printf("%lld\n",path_query(x,y));
198 }
199 else{
200 read(x);read(y);read(z);
201 if(pos[x]<pos[y])tree.update(1,1,n,pos[y],z);
202 else tree.update(1,1,n,pos[x],z);
203 }
204 }
205
206 return 0;
207 }
208
209 inline void addedge(int x,int y,int z){
210 pe++;
211 u[pe]=x;
212 v[pe]=y;
213 w[pe]=z;
214 ne[pe]=fi[x];
215 fi[x]=pe;
216 }
217
218 void dfs1(int s){
219 size[s]=1;
220 int maxson=0;
221
222 int t=fi[s];
223 int to=v[t];
224
225 while(t){
226 if(to!=fa[s]){
227 fa[to]=s;
228 dep[to]=dep[s]+1;
229 wp[to]=w[t];
230
231 dfs1(to);
232
233 size[s]+=size[to];
234 if(size[to]>maxson){
235 son[s]=to;
236 maxson=size[to];
237 }
238 }
239
240 t=ne[t];
241 to=v[t];
242 }
243 }
244
245 void dfs2(int s){
246 xu++;
247 dfsx[xu]=s;
248
249 if(son[s]){
250 top[son[s]]=top[s];
251 dfs2(son[s]);
252 }
253
254 int t=fi[s];
255 int to=v[t];
256
257 while(t){
258 if(to!=fa[s] && to!=son[s]){
259 top[to]=to;
260 dfs2(to);
261 }
262
263 t=ne[t];
264 to=v[t];
265 }
266 }
267
268 inline ll path_query(int x,int y){
269 if(x==y)return 0; //特判起终点相同
270
271 pl=pr=0; //重置临时结果数组
272
273 int tx=top[x],ty=top[y];
274
275 while(tx!=ty){
276 if(dep[tx]>dep[ty]){
277 pl++;
278 TL[pl]=tree.query(1,1,n,pos[tx],pos[x]);
279 x=fa[tx];
280 tx=top[x];
281 }
282 else{
283 pr++;
284 TR[pr]=tree.query(1,1,n,pos[ty],pos[y]);
285 y=fa[ty];
286 ty=top[y];
287 }
288 }
289
290 if(x!=y){
291 if(pos[x]<pos[y]){
292 pr++;
293 TR[pr]=tree.query(1,1,n,pos[x]+1,pos[y]);
294 }
295 else{
296 pl++;
297 TL[pl]=tree.query(1,1,n,pos[y]+1,pos[x]);
298 }
299 }
300
301 for(ri i=2;i<=pl;i++)TL[1].merge(TL[i],TL[1]);
302 for(ri i=2;i<=pr;i++)TR[1].merge(TR[i],TR[1]);
303 //这里全部都要注意运算顺序!
304 if(!pl)return TR[1].ans;
305 else if(!pr)return TL[1].ans; //特判答案仅在lca一侧的情况
306 else{
307 for(ri i=0;i<10;i++){
308 swap(TL[1].p0[i],TL[1].s0[i]);
309 swap(TL[1].p1[i],TL[1].s1[i]);
310 } //“翻转”TL[1]
311
312 TL[1].merge(TL[1],TR[1]);
313 return TL[1].ans;
314 }
315 }
来源:oschina
链接:https://my.oschina.net/u/4280700/blog/4196131