题意:
给出一个完全图,边权为两点权值的and,求最大生成树。
题解:
这题用最小生成树的Boruvka算法。
大概就是每次找到每一个联通块权值最大的边,将这些联通块合并,直到只剩一个联通块。因为每次联通块的个数至少减半,所以只会做log次操作。
那么这题相当于每个点有不同颜色,要找到每个点的异色点中最大的and值。
扔题解跑
code:
#include<cstdio>
#include<cstdlib>
#include<cstdlib>
#include<iostream>
#define LL long long
using namespace std;
int n,m,fa[100010];
int a[100010];
int findfa(int x) {return fa[x]==x?x:fa[x]=findfa(fa[x]);}
struct node{
int max,min;
node() {max=0;min=100000;}
node(int x,int y) {max=x;min=y;}
}mx[100010];
struct trnode{
int lc,rc;
node c;
}tr[1000010];int tot,root;
int new_node()
{
int x=++tot;
tr[x].lc=tr[x].rc=tr[x].c.max=0;tr[x].c.min=n;
return x;
}
node update(node lc,node rc)
{
node ans;
ans.max=max(lc.max,rc.max);
ans.min=min(lc.min,rc.min);
return ans;
}
void cmin(int &x,int y) {x=x<y?x:y;}
void cmax(int &x,int y) {x=x>y?x:y;}
void ins(int &x,int dep,int c,int id)
{
if(!x) x=new_node();
cmin(tr[x].c.min,id);cmax(tr[x].c.max,id);
if(dep<0) return;
if(c&(1<<dep)) ins(tr[x].rc,dep-1,c,id);
else ins(tr[x].lc,dep-1,c,id);
}
int merge(int x,int y)
{
if(!y) return x;
if(!x) x=new_node();
tr[x].c=update(tr[x].c,tr[y].c);
tr[x].lc=merge(tr[x].lc,tr[y].lc);
tr[x].rc=merge(tr[x].rc,tr[y].rc);
return x;
}
node findans(int x,int dep,int c,int id)
{
if(dep<0) return node(0,tr[x].c.min==id?tr[x].c.max:tr[x].c.min);
node ans;
if(c&(1<<dep))
if(tr[x].rc&&(id!=tr[tr[x].rc].c.min||id!=tr[tr[x].rc].c.max)) ans=findans(tr[x].rc,dep-1,c,id),ans.max+=(1<<dep);
else ans=findans(tr[x].lc,dep-1,c,id);
else ans=findans(tr[x].lc,dep-1,c,id);
return ans;
}
void dfs(int x,int dep)
{
if(dep<0) return;
if(tr[x].lc) dfs(tr[x].lc,dep-1);
if(tr[x].rc) dfs(tr[x].rc,dep-1);
tr[x].lc=merge(tr[x].lc,tr[x].rc);
}
void build()
{
tot=0;root=new_node();
for(int i=1;i<=n;i++) ins(root,m-1,a[i],findfa(i));
dfs(root,m-1);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),fa[i]=i;
int now=n;LL ans=0;
while(now>1)
{
build();
for(int i=1;i<=n;i++) mx[i]=node(-1,0);
for(int i=1;i<=n;i++)
{
node u=findans(root,m-1,a[i],findfa(i));
if(u.max>mx[findfa(i)].max) mx[findfa(i)]=u;
}
for(int i=1;i<=n;i++)
if(fa[i]==i)
{
int x=findfa(mx[i].min);
if(x!=i) fa[i]=x,now--,ans+=mx[i].max;
}
}
printf("%lld",ans);
}