题目来源:CF 888G
思路简析:用Trie加一丢丢位压加一丢丢树上search
结构体(二维数组也可以(我脑抽了))&位压:
struct node{int s[2];}trie[30*N+5];
...
balabalabalala
...
void insert(int x){//有点位压的意思,就是太乱
int w[31],Id=1,c=1,now=0;
memset(w,0,sizeof(w));
while(x) w[Id++]=x%1,x/=2;
for(int i=30;i>=1;--i)//存到结构体里
if(!trie[now].s[w[i]]){//如果没有
trie[now].s[w[i]]=c;//存入
now=c++;//c更新同时now=新的c
}
else now=trie[now].s[w[i]];
return;
}
search:
ll ans=0;
ll find(int x,int y,int d)//类似于搜索
{
if(!(trie[x].s[0]&&trie[x].s[1]))return 0;//x 1 y 2都不存在
ll res=INF;bool f=0;
if(trie[x].s[0]&&trie[y].s[0]){//x 1 y 1存在
f=1;
res=min(res,find(trie[x].s[0],trie[y].s[0],d-1));
}
if(trie[x].s[1]&&trie[y].s[1]){//x 2 y 2存在
f=1;
res=min(res,find(trie[x].s[1],trie[y].s[1],d-1));
}
if(!f){//存在一个
if(trie[x].s[0]&&trie[y].s[1])//x 1 y 2
res=min(res,find(trie[x].s[0],trie[y].s[1],d-1));
if(trie[x].s[1]&&trie[y].s[0])//x 2 y 1
res=min(res,find(trie[x].s[1],trie[y].s[0],d-1));
res+=Pow[d];
}
return res;
}
void dfs(int now,int d)//普通的二叉树dfs遍历
{
if(trie[now].s[0]&&trie[now].s[1])
ans+=Pow[d]+find(trie[now].s[0],trie[now].s[1],d-1);//两者都存在,加入答案;
if(trie[now].s[0])//有一个,走
dfs(trie[now].s[0],d-1);
if(trie[now].s[1])//有另一个,走
dfs(trie[now].s[1],d-1);
}
完整代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf=1e18;
const int N=200010;
int a[N];
struct node{int s[2];}trie[30*N+5];
void insert(int x){//有点位压的意思,就是太乱
int w[31],Id=1,c=1,now=0;
memset(w,0,sizeof(w));
while(x) w[Id++]=x%1,x/=2;
for(int i=30;i>=1;--i)//存到结构体里
if(!trie[now].s[w[i]]){//如果没有
trie[now].s[w[i]]=c;//存入
now=c++;//c更新同时now=新的c
}
else now=trie[now].s[w[i]];
return;
}
int Pow[36];
ll ans=0;
ll find(int x,int y,int d)//类似于搜索
{
if(!(trie[x].s[0]&&trie[x].s[1]))return 0;//x 1 y 2都不存在
ll res=INF;bool f=0;
if(trie[x].s[0]&&trie[y].s[0]){//x 1 y 1存在
f=1;
res=min(res,find(trie[x].s[0],trie[y].s[0],d-1));
}
if(trie[x].s[1]&&trie[y].s[1]){//x 2 y 2存在
f=1;
res=min(res,find(trie[x].s[1],trie[y].s[1],d-1));
}
if(!f){//存在一个
if(trie[x].s[0]&&trie[y].s[1])//x 1 y 2
res=min(res,find(trie[x].s[0],trie[y].s[1],d-1));
if(trie[x].s[1]&&trie[y].s[0])//x 2 y 1
res=min(res,find(trie[x].s[1],trie[y].s[0],d-1));
res+=Pow[d];
}
return res;
}
void dfs(int now,int d)//普通的二叉树dfs遍历
{
if(trie[now].s[0]&&trie[now].s[1])
ans+=Pow[d]+find(trie[now].s[0],trie[now].s[1],d-1);//两者都存在,加入答案;
if(trie[now].s[0])//有一个,走
dfs(trie[now].s[0],d-1);
if(trie[now].s[1])//有另一个,走
dfs(trie[now].s[1],d-1);
}
int main()
{
int n;cin>>n;
Pow[1]=1;
for(int i=2;i<=30;++i)
Pow[i]=Pow[i-1]*2;//和HASH 差不多,Pow用于“解压”
for(int i=0;i<n;++i)
{
int x;scanf("%d",&x);
insert(x);//位压缩
}
dfs(0,30);//节点, step(2^30)
cout<<ans<<endl;
return 0;
}