GJGHFD的最小樹 題解 [Trie樹+啟發式合併]
阿新 • • 發佈:2020-12-29
GJGHFD的最小樹
Description:
給定一棵\(n\)個結點的樹,結點標號為 \(0,1, · · · ,n−1\) 並且樹上每條邊有一個權值 \(w_i\),你可以不斷向這張圖加入一條權值為任意非負整數的邊,或者從圖裡刪除一條邊,但是在任意時刻以下條件都必須滿足:
- 這張圖是連通的.
- 對於這張圖中的任意一個環,環上所有邊的異或和必須為 \(0\) .
求最終圖中所有邊權值和的最小值.
Input:
第一行一個整數 \(n\),表示樹的大小.
接下來 \(n−1\) 行,每行三個整數 \(x, y, z\),表示樹中有一條連線結點 \(x, y\)的邊,邊權為 \(z\)
Output:
輸出一行一個整數表示答案.
Sample Input:
6
0 1 1
1 2 4
1 3 3
0 4 5
0 5 2
Sample Output:
7
Hint:
對於\(20\%\)的資料,\(2 \leq n \leq 10\)
對於\(50\%\)的資料,\(2 \leq n \leq 1000\)
對於\(100\%\)的資料,$ 2\leq n \leq 10^5.0 \leq z < 2^{30}$
時間限制: \(1s\)
空間限制: \(512M\)
題目分析:
可以發現,當加入一條邊\((u, v)\)時,其邊權一定等於原樹中從\(u\)到\(v\)
程式碼如下(馬蜂很醜,不喜勿噴)——
#include<bits/stdc++.h> #define Tp template<typename T> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define maxn 100005 #define inf 2147483647 #define LL long long using namespace std; int n,tot,a[maxn],fir[maxn],son[maxn<<1],nxt[maxn<<1],w[maxn<<1],s[maxn*30],ch[maxn*30][2],pw[maxn];LL ans; inline void insert(int x){int now=0;for(register int i=29,to;i>=0;i--) to=((x>>i)&1),(!ch[now][to])&&(ch[now][to]=++tot),now=ch[now][to],s[now]++;} inline void add(int x,int y,int z){son[++tot]=y,nxt[tot]=fir[x],fir[x]=tot,w[tot]=z;} inline int dfs(int x,int y,int dep){ if(dep==31) return 0;int res=inf;if(ch[x][0]) if(ch[y][0]) res=min(res,dfs(ch[x][0],ch[y][0],dep+1));else res=min(res,dfs(ch[x][0],ch[y][1],dep+1)+pw[30-dep]); if(ch[x][1]) if(ch[y][1]) res=min(res,dfs(ch[x][1],ch[y][1],dep+1));else res=min(res,dfs(ch[x][1],ch[y][0],dep+1)+pw[30-dep]);return res; } inline void solve(int x,int dep){ if(!x&&dep>1) return;solve(ch[x][0],dep+1),solve(ch[x][1],dep+1);if(!ch[x][0]||!ch[x][1]) return; if(s[ch[x][0]]>s[ch[x][1]]) ans+=dfs(ch[x][1],ch[x][0],dep+1)+pw[30-dep];else ans+=dfs(ch[x][0],ch[x][1],dep+1)+pw[30-dep]; } inline void get(int x,int fa){insert(a[x]);for(register int i=fir[x];i;i=nxt[i]) if(son[i]!=fa) a[son[i]]=(a[x]^w[i]),get(son[i],x);} class FileInputOutput { private: static const int S=1<<21; #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++) #define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch)) char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[25]; public: FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; } Tp inline void read(T& x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc())); } Tp inline void write(T x,const char& ch) { if (x<0) pc('-'),x=-x; RI ptop=0; while (pt[++ptop]=x%10,x/=10); while (ptop) pc(pt[ptop--]+48); pc(ch); } inline void flush(void) { fwrite(Fout,1,Ftop-Fout,stdout); } #undef tc #undef pc }F; int main(){ // freopen("data.in","r",stdin); F.read(n);pw[0]=1;for(register int i=1;i<=29;i++) pw[i]=(pw[i-1]<<1); for(register int i=1,x,y,z;i<n;i++) F.read(x),F.read(y),F.read(z),add(x,y,z),add(y,x,z);tot=0;get(0,-1); /*for(register int i=1;i<=n;i++) F.read(a[i]),insert(a[i]);*/solve(0,1);F.write(ans,'\n');return F.flush(),0; }