洛谷T3401 洛谷樹 樹剖&&分治

這道題好乾燥啊。。。折騰了半個月。。。感謝bogo大佬對個人指導。。。
題目要求支持的操做:1.查詢某段路徑的全部子路徑的xor值之和;2.修改某條邊的權值。重點是操做1。
剛開始,我看到了操做1以後就不自覺的想到了非~常暴力的東西。。。還好大佬及時把我引上正途:分治!node


你們知道,最大子段和有個分治算法,本題的方法就跟這個比較相似。
對於一段子路徑,若它能對答案產生貢獻,那麼它要麼徹底在左兒子中,要麼徹底在右兒子中,要麼跨越左右兒子。
對於每段路徑,咱們須要記錄以下變量: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和0異或的結果是1嘛,所以就對答案產生了貢獻。
咱們固然也要維護p0、p一、s0、s1。這裏較上面簡單一些,細節詳見代碼的rg_a結構體定義部分。
以上討論的都是鏈上的作法,題目給定的是一棵樹,那麼樹剖就Ok了,以後扔到線段樹裏大力merge便可~
對於修改操做,在線段樹底層重建節點,而後順次merge其全部祖先便可。ios


下面說幾個疑難的問題:
一.要建線段樹,要求權值在點上,但題目卻說在邊上。怎麼辦呢?能夠把每條邊的邊權下放到樹中此條邊下方鏈接的節點上。這樣,根節點就不會被下放,可是並不影響結果,至於爲何,會在下面提到。
二.查詢時,對於一段路徑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 }
相關文章
相關標籤/搜索