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;
}
posted @ 2020-12-29 20:50  OdtreePrince  阅读(84)  评论(0编辑  收藏  举报