咕了好久终于来写了
题意
给你个点的无向完全图,每个点有点权,每两个点之间的边权是点权的
值。问最小生成树边权和。
思路
从别人那里学来的巧妙思路,我们考虑树,首先,我们先把样例从高位到低位插入线性基。容易发现,对于每个叶子节点,既每个点值之间,是需要互相连边,那么求他们
以后的边的异或值即可。由此可得,若是
的深度越深,便越优。因为我们是从高位到低位插入的,所以浅的点权值较大,要尽量避免选择浅的点。我们把可能是
的点找出来,可以发现如果
均不相等那么这样的点就恰好有
个。如果有
,我们建一条边连接
,
,权值为
,对答案完全没有影响。
我们每找到这样的点,就暴力贪心下去:
每次尽量同时走左儿子或右儿子;
如果两个都有,就两个都走,然后取 。
如果两个只有不一样的儿子,就在返回值加上这一深度的值,然后继续走
代码
#include<bits/stdc++.h>
#define int long long
const int N=1e5+5,INF=0x3f3f3f3f,mod=998244353;
using namespace std;
int n;
int son[2][N*30+10],tot,ans;
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*f;
}
int qpow(int a,int b)
{
int ans=1;
while(b){if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}
return ans;
}
void Insert(int a)
{
int now=0,id;
for(int i=30;i>=0;i--)
{
id=(a>>i)&1;
if(!son[id][now])son[id][now]=++tot;
now=son[id][now];
}
}
int find(int r1,int r2,int b)
{
if(b<0) return 0;
int a1=-1,a2=-1;
if(son[0][r1]&&son[0][r2]) a1=find(son[0][r1],son[0][r2],b-1);
if(son[1][r1]&&son[1][r2]) a2=find(son[1][r1],son[1][r2],b-1);
if(~a1&&~a2) return min(a1,a2);
if(~a1) return a1;
if(~a2) return a2;
if(son[1][r1]&&son[0][r2]) a1=find(son[1][r1],son[0][r2],b-1)+(1<<b);
if(son[0][r1]&&son[1][r2]) a2=find(son[0][r1],son[1][r2],b-1)+(1<<b);
if(~a1&&~a2) return min(a1,a2);
if(~a1) return a1;
if(~a2) return a2;
}
void dfs(int a,int b)
{
if(b<0) return;
if(son[0][a]&&son[1][a]) ans+=1ll*find(son[0][a],son[1][a],b-1)+(1ll<<b);
if(son[0][a]) dfs(son[0][a],b-1);
if(son[1][a]) dfs(son[1][a],b-1);
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
Insert(read());
dfs(0,30);
printf("%lld\n",ans);
return 0;
}

京公网安备 11010502036488号