1. 程式人生 > 實用技巧 >GJGHFD的最小樹 題解 [Trie樹+啟發式合併]

GJGHFD的最小樹 題解 [Trie樹+啟發式合併]

GJGHFD的最小樹

Description:

​ 給定一棵\(n\)個結點的樹,結點標號為 \(0,1, · · · ,n−1\) 並且樹上每條邊有一個權值 \(w_i\),你可以不斷向這張圖加入一條權值為任意非負整數的邊,或者從圖裡刪除一條邊,但是在任意時刻以下條件都必須滿足:

  1. 這張圖是連通的.
  2. 對於這張圖中的任意一個環,環上所有邊的異或和必須為 \(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\)

邊權的異或和. 如果我們賦予樹上每個結點一個權值,使得任意兩個相鄰結點權值的異或等於連線他們的邊的權值,那麼原問題就轉化為一個\(xor\)最小生成樹了,使用啟發式合併\(+\)字典樹即可解決. 至於如何賦權,DFS 一遍將每個結點的權值設為從根到它路徑上邊權的異或和即可.

​ 程式碼如下(馬蜂很醜,不喜勿噴)——

#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;
}