小奇的倉庫(樹形DP)
「題目背景」
小奇採的礦實在太多了,它準備在喵星系建個礦石倉庫。令它無語的是,喵星系的貨運飛船引擎還停留在上元時代!
「問題描述」
喵星系有n個星球,星球以及星球間的航線形成一棵樹。
從星球a到星球b要花費[dis(a,b) Xor M]秒。(dis(a,b)表示ab間的航線長度,Xor為位運算中的異或)
為了給倉庫選址,小奇想知道,星球i(1<=i<=n)到其它所有星球花費的時間之和。
「輸入格式」
第一行包含兩個正整數n,M。
接下來n-1行,每行3個正整數a,b,c,表示a,b之間的航線長度為c。
「輸出格式」
n行,每行一個整數,表示星球i到其它所有星球花費的時間之和。
「樣例輸入」
4 0
1 2 1
1 3 2
1 4 3
「樣例輸出」
6
8
10
12
「資料範圍」
序號 N M
1 6 0
2 100 5
3 2000 9
4 50000 0
5 50000 0
6 50000 1
7 50000 6
8 100000 10
9 100000 13
10 100000 15
保證答案不超過2*10^9
下面一段話是出題人神祕而不失優雅的題解
演算法1:
不會寫函式的小夥伴們,我們只需要寫個floyd,就有10分啦!
演算法2:
在演算法1的基礎上,我們對每條邊處理一下xor,就有20分啦!
演算法3:
簡單的樹形DP,或者你會nlogn的dij,處理完每個點到其它點的最短路後再加上xor,那麼這樣就有30分啦!
演算法4:
第4、5個點無需xor,那麼我們樹形DP掃一個節點與其它所有節點的路徑長度之和,可以合併資訊,最終均攤O(1),50分到手。
演算法5:
第6個點xor 1,那麼我們樹形DP到一個點時記錄有多少個0,多少個1,然後每當一條路徑到2,那部分就再記錄一個值,60分到手。
演算法6:
如果你第6個點都過了,卻沒有滿分,笨死啦!
一樣的嘛,就是原來的“0”、“1”、大於等於2變成了0~16麼~~
下面是自己的話:
既然是棵樹,又要快速地求每個點的值,那一定是樹形DP加上換根的操作啦~
但是異或m要怎麼處理呢?可以觀察資料規模,發現m最大最大也就15,換成二進位制數也就是 1111,所以發現異或m最多隻會對數字的後面4位造成影響(異或0甚至無法造成什麼影響)
於是愉快地寫出DP陣列 f[i]和sz[i][0~15]
f表示此時以i為根的子樹到i節點的距離之和(減去後綴後的和)
sz表示此時距離以j為字尾的共有幾個
每一次向根節點方向轉移時會加上一條邊的長度,此時不同字尾距離的字尾會發生相應改變,然後更新父親相應字尾的sz值。
然後f裡統計的距離總和是抹掉所有後綴後的總和,即不考慮字尾的貢獻。如有一個距離是 10111(2),抹去長度為2的字尾後就只剩下10100,然後將這個結果加到f數組裡,到最後根節點統計最終答案時再考慮每個字尾的貢獻,此時的sz陣列就派上用場了(具體看程式碼)
還有一點,就是最後要把答案減去m,因為統計字尾貢獻時,多加了自己到自己的距離(本來為0,xor m 後變成了m)。
程式碼如下
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 7 #define For(i,a,b) for(register int i=a;i<=b;++i) 8 #define Re register 9 #define Pn putchar('\n') 10 #define inf 0x7f7f7f 11 #define llg long long 12 using namespace std; 13 const int N=1e5+10; 14 int sz[N][17]; 15 int head[N],nxt[N*2],v[N*2],cnt=1; 16 llg w[N*2],z,fn[N],f[N]; 17 int n,m,x,y,ct,tot; 18 19 inline void read(int &v){ 20 v=0; 21 char c=getchar(); 22 while(c<'0'||c>'9')c=getchar(); 23 while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar(); 24 } 25 inline void read(llg &v){ 26 v=0; 27 char c=getchar(); 28 while(c<'0'||c>'9')c=getchar(); 29 while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar(); 30 } 31 void write(llg x){ 32 if(x>9)write(x/10); 33 int xx=x%10; 34 putchar(xx+'0'); 35 } 36 37 void add(int ux,int vx,llg wx){ 38 cnt++; 39 nxt[cnt]=head[ux]; head[ux]=cnt; v[cnt]=vx; w[cnt]=wx; 40 cnt++; 41 nxt[cnt]=head[vx]; head[vx]=cnt; v[cnt]=ux; w[cnt]=wx; 42 } 43 44 void DFS1(int x,int fa){ 45 sz[x][0]=1; 46 for(Re int i=head[x];i;i=nxt[i]){ 47 int vv=v[i]; 48 if(vv==fa)continue; 49 DFS1(vv,x); 50 f[x]+=f[vv]; 51 For(j,0,tot){ 52 int Nsm=j+w[i]; 53 int Nst=Nsm & ct; 54 sz[x][Nst]+=sz[vv][j]; 55 f[x]+=sz[vv][j]*(Nsm-Nst); 56 } 57 } 58 } 59 int Bsz[N][20]; 60 void DFS2(int x,int fa){ //換根 61 fn[x]=f[x]; 62 For(st,0,tot){ 63 fn[x]+=(st^m)*sz[x][st]; 64 } 65 For(st,0,tot)Bsz[x][st]=sz[x][st]; 66 llg Bf=f[x]; 67 68 for(Re int i=head[x];i;i=nxt[i]){ 69 int vv=v[i]; 70 if(vv==fa)continue; 71 72 int Nsm,Nst; 73 74 For(st,0,tot){ 75 Nsm =st+w[i]; 76 Nst=Nsm&ct; 77 sz[x][Nst]-=sz[vv][st]; 78 f[x]-=sz[vv][st]*(Nsm-Nst); 79 } 80 81 f[vv]=f[x]; 82 For(st,0,tot){ 83 Nsm=st+w[i]; 84 Nst=Nsm&ct; 85 sz[vv][Nst]+=sz[x][st]; 86 f[vv]+=sz[x][st]*(Nsm-Nst); 87 } 88 89 DFS2(vv,x); 90 91 f[x]=Bf; 92 For(st,0,tot)sz[x][st]=Bsz[x][st]; 93 } 94 } 95 96 int main(){ 97 // freopen("warehouse.in","r",stdin); 98 // freopen("warehouse.out","w",stdout); 99 read(n); read(m); 100 101 if(m==0)ct=0,tot=0; 102 if(m==1)ct=1,tot=1; 103 if(m==5)ct=7,tot=7; 104 if(m==6)ct=7,tot=7; 105 if(m>=9)ct=15,tot=15; //簡單粗暴的預處理 106 107 For(i,1,n-1){ 108 read(x); read(y); read(z); 109 add(x,y,z); 110 } 111 DFS1(1,0); 112 DFS2(1,0); 113 For(i,1,n){ 114 write(fn[i]-m); Pn; 115 } 116 return 0; 117 }