洛谷T3401 洛谷树 树剖&&分治

这道题好干燥啊。。。折腾了半个月。。。感谢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 }

原文地址:https://www.cnblogs.com/running-coder-wfh/p/8242103.html

时间: 2024-10-14 04:54:58

洛谷T3401 洛谷树 树剖&&分治的相关文章

【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】

题目链接: TP 题解:   可能是我比较纱布,看不懂题解,只好自己想了…… 我们考虑对于询问区间是可以差分的,然而这并没有什么卵用,然后考虑怎么统计答案. 首先LCA一定是z的祖先(这里说的祖先包括自己,以下祖先均为此概念)节点,也就是是说我们只要计算出每个祖先节点的贡献就可以了,再考虑每个祖先的贡献如何计算. 我们发现对于深度其实是该点到root的路径点数,所以我们可以这样想,我们询问z的祖先的答案,就是在计算有对于给定区间有多少个点经过了z的祖先. 那么思路到这里就很清晰了,我们先把每个点

CF487E Tourists 【圆方树 + 树剖 + 堆】

题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用了一对一的特性优化了复杂度 如此询问时如果\(lca\)为方点,再询问一下\(lca\)的父亲即可 复杂度\(O(qlog^2n)\) #include<algorithm> #include<iostream> #include<cstring> #include<

【bzoj3672】[Noi2014]购票 斜率优化+CDQ分治+树的点分治

题目描述 给出一棵以1为根的带边权有根树,对于每个根节点以外的点$v$,如果它与其某个祖先$a$的距离$d$不超过$l_v$,则可以花费$p_vd+q_v$的代价从$v$到$a$.问从每个点到1花费的最小代价(中途可以经停其它点) 输入 第 1 行包含2个非负整数 n,t,分别表示城市的个数和数据类型(其意义将在后面提到).输入文件的第 2 到 n 行,每行描述一个除SZ之外的城市.其中第 v 行包含 5 个非负整数 $f_v,s_v,p_v,q_v,l_v$,分别表示城市 v 的父亲城市,它到

acm 2015北京网络赛 F Couple Trees 主席树+树链剖分

提交 题意:给了两棵树,他们的跟都是1,然后询问,u,v 表 示在第一棵树上在u点往根节点走 , 第二棵树在v点往根节点走,然后求他们能到达的最早的那个共同的点 解: 我们将第一棵树进行书链剖,然后第二棵树采用主席树,他的信息来自他的父亲节点,每个点存他在第一棵树 树链剖分后的位置,这样我们每次查询uv的时候我们只要 我们选取u和top[u]这段区间在主席树v这颗树上找,在这个区间能取到的最大值,一旦存在,这个最大值就我们要的,这个点保存着他到根节点这条路上所有点在第一棵树剖分后的位置 #inc

【BZOJ2325】[ZJOI2011]道馆之战 线段树+树链剖分

[BZOJ2325][ZJOI2011]道馆之战 Description 口袋妖怪(又名神奇宝贝或宠物小精灵)红/蓝/绿宝石中的水系道馆需要经过三个冰地才能到达馆主的面前,冰地中的每一个冰块都只能经过一次.当一个冰地上的所有冰块都被经过之后,到下一个冰地的楼梯才会被打开.三个冰地分别如下: 当走出第三个冰地之后,就可以与馆主进行道馆战了.馆主发现这个难度太小,导致经常有挑战者能通过,为了加大难度,将道馆分成了n个房间,每个房间中是两个冰块或障碍,表示一列冰地.任意两个房间之间均有且仅有一条路径相

hdu_5314_Happy King(树的点分治)

题目链接:hdu_5314_Happy King 题意: 给出一颗n个结点的树,点上有权值: 求点对(x,y)满足x!=y且x到y的路径上最大值与最小值的差<=D: 题解: 还是树的点分治,在统计答案的时候先按到根的最小值排序,然后用最大值减D去找有多少个满足答案. 1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 typedef pair<i

bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 &amp;&amp; AC400

3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec  Memory Limit: 512 MBSubmit: 159  Solved: 40[Submit][Status][Discuss] Description 强 强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上是一个带权树.每个时刻它会长出一个新的叶子节点.每个节点上有一个可爱的小精灵,

bzoj 2152: 聪聪可可 树的点分治

2152: 聪聪可可 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 485  Solved: 251[Submit][Status] Description 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏.他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“

哈夫曼树树状输出

1 #include "stdio.h" 2 #include "malloc.h" 3 4 #define maxlen 100 5 #define infinity 65535 6 7 struct bnode 8 { 9 int data;//数据 10 bnode *lchild,*rchild; 11 bool flags;//使用标志 12 }; 13 14 bnode *tree[maxlen]; 15 16 void initialization(i