这题是51nod上一个题目稍微变形了一下:完全图最小生成树
原题是 对于一个完全图 每条边(u,v)的边权都是 a[u]^a[v] 求一个最小生成树
但是如果暴力去做的话 肯定会爆时间 n=1e5 边数就是n的平方级别的 我们需要去优化建立生成树的过程
有一个明显的想法是 从最高位开始 我们把当前位为0的分成一块 为1的分成一块 那么肯定是所有为1的在一个联通块内 所有为0的在一个联通块内 因为这样的话 为1的联通块的边权会尽量小 为0的联通块也是
那么要连接这两个联通块 就需要在他们之间 建立一条边 显然我们可以先把 为0的联通块的点权放入trie 然后再遍历为1的联通块的点权 得到一个最小异或值 即可
这样的话我们可以得到一个分治思路 从最高位开始 每次分成01 两块 在这两块中建立最小边权 在递归解决两个子问题即可 然后这道题还需要计数 计数的话有个公式 完全图的生成树个数为 : n^(n-2) 种
不过牛客的题没有计数 会简便很多 每次加上最小边权就可以了
复杂度是 n(logn^2) 的
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int h[N],nex[N<<1],to[N<<1],edge[N<<1];
int trie[N*32][2],tot,a[N],n,s[N],t[N],cur;
const int inf=(1<<30);
typedef long long ll;
ll sum;
inline int in(){
int x=0;char c=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void add_edge(int x,int y,int z){
to[++cur]=y;nex[cur]=h[x];edge[cur]=z;h[x]=cur;
}
void dfs(int u,int f){
for(int i = h[u]; i; i = nex[i]){
int v = to[i];
if(v==f) continue;
a[v]=a[u]^edge[i];
dfs(v,u);
}
}
void init(){
for(int i = 0; i <= tot; i++) trie[i][0]=trie[i][1]=0;
tot=0;
}
void ins(int x){
int p=0;
for(int i = 29; i >= 0; i--){
int c=(x>>i)&1;
if(!trie[p][c]) trie[p][c]=++tot;
p=trie[p][c];
}
}
inline int getmi(int x){
int ans=0,p=0;
for(int i = 29; i >= 0; i--){
int c = (x>>i)&1;
if(trie[p][c]) ans|=c<<i,p=trie[p][c];
else ans|=(c^1)<<i,p=trie[p][c^1];
}
return (ans^x);
}
void solve(int st,int ed,int now){
if(st>=ed) return;
if(now<0) return;
int cnt1=0,cnt2=0;
for(int i = st; i <= ed; i++)
if((a[i]>>now)&1) s[++cnt1]=a[i];
else t[++cnt2]=a[i];
for(int i = 1; i <= cnt1; i++) a[i+st-1]=s[i];
for(int i = 1; i <= cnt2; i++) a[st+cnt1+i-1]=t[i];
init();
for(int i = 1; i <= cnt2; i++) ins(t[i]);
int mi=inf;
if(cnt2){
for(int i = 1; i <= cnt1; i++){
int k=getmi(s[i]);
mi=min(mi,k);
}
}
if(mi!=inf) sum+=mi;
solve(st,st+cnt1-1,now-1);solve(st+cnt1,ed,now-1);
}
int main(){
n=in();
for(int i = 1; i < n; i++){
int u,v,w;
u=in(),v=in(),w=in();
u++,v++;
add_edge(u,v,w);
add_edge(v,u,w);
}
dfs(1,0);
solve(1,n,29);
printf("%lld\n",sum);
return 0;
}