题意:一个n个点的完全图,点带权,边权是两端点点权的异或值。问你最小生成树。
一个性质,把所有点按照二进制最高位是否为1划分为2个集合,那么这两个集合间只会有一条边。可以递归处理。
把所有点建成01Trie,发现两个集合就是Trie的每个结点的两个子树。用启发式的思想,在小子树里dfs到叶子结点,取出每个值,然后去大子树里查询即可。
O(n(logn)^2)。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; int ch[100005*32][2],sz,siz[100005*32]; void Insert(int x) { int U=0; for(int i=31-1;i>=0;--i){ if(!ch[U][(x>>i)&1]){ ch[U][(x>>i)&1]=++sz; } U=ch[U][(x>>i)&1]; } } int query(int x,int U,int nidep){ int res=0; for(int i=nidep;i>=0;--i){ bool Bit=((x>>i)&1); if(!ch[U][Bit]){ res|=(1<<i); Bit^=1; } U=ch[U][Bit]; } return res; } ll ans; void dfsz(int U){ if(!ch[U][0] && !ch[U][1]){ siz[U]=1; } if(ch[U][0]){ dfsz(ch[U][0]); siz[U]+=siz[ch[U][0]]; } if(ch[U][1]){ dfsz(ch[U][1]); siz[U]+=siz[ch[U][1]]; } } int nowans; void df2(int U,int now,int rtnidep,int nidep,int otherrt){ if(!ch[U][0] && !ch[U][1]){ nowans=min(nowans,query(now,otherrt,rtnidep-1)); } if(ch[U][0]){ df2(ch[U][0],now,rtnidep,nidep-1,otherrt); } if(ch[U][1]){ df2(ch[U][1],now|(1<<(nidep-1)),rtnidep,nidep-1,otherrt); } } void dfs(int U,int nidep){ if(ch[U][0] && ch[U][1]){ nowans=2147483647; if(siz[ch[U][0]]>siz[ch[U][1]]){ df2(ch[U][1],0,nidep,nidep,ch[U][0]); } else{ df2(ch[U][0],0,nidep,nidep,ch[U][1]); } nowans|=(1<<nidep); ans+=nowans; } if(ch[U][0]){ dfs(ch[U][0],nidep-1); } if(ch[U][1]){ dfs(ch[U][1],nidep-1); } } int n,m; int main(){ // freopen("b.in","r",stdin); int x; scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&x); Insert(x); } dfsz(0); dfs(0,30); printf("%lld ",ans); return 0; }