传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6625
比赛的时候想出了一个和正解差不多的类似贪心,但是实现非常麻烦,而且最后半个小时实在是写不完,写到还剩10分钟的时候放弃了。
赛后打开dls直播间,看着dls画了张图,讲了一句话,秒懂,果然这种肯定是有一种很简单的贪心的,但感觉总离想到差一点。
首先考虑贪心目的,我们要字典序最小,那么就要小得尽量小,小的尽量多
建立tra,trb两棵trie,然后开始dfs,a1->bi->ai->bj->aj....bi是a1在trb中匹配出来的异或最小的,ai是bi在tra中匹配出来的使抑或和最小的,w1=bi^a1 ,w2=ai^bi ,w3=bj^ai....,一定有w1<w2<w3...的关系。那么如果dfs到某个时刻 ai->bj->aj , aj 与 ai 是同一个值,那么这个时候选择 aj 与 bj 配对一定没错,因为他们互为在对方集合中找出的异或最小值。
于是就是不停地删除和查询,n对数字配对完就行,复杂度O(n *30)
其实还有很多其他的题,也可以用相同的贪心模型,就是两个集合配对,然后尽量小的问题,都可以这样去dfs贪心,成为一个2大小的环的时候就匹配成功,问题不是异或和都行。
#include<bits/stdc++.h>
#define maxl 100010
using namespace std;
int n,m,tota,totb,sum;
int ans[maxl],mi[30];
int a[maxl],b[maxl];
int suma[maxl*31],sumb[maxl*31];
int tra[maxl*31][2],trb[maxl*31][2];
int q[maxl],w[maxl];
vector <int> numa[maxl*31],numb[maxl*31];
vector <int> :: iterator it;
bool ina[maxl],inb[maxl];
inline void inserta(int id,int x)
{
int u=0,c;
for(int i=29;i>=0;i--)
{
suma[u]++;
c=(x>>i)&1;
if(!tra[u][c])
tra[u][c]=++tota;
u=tra[u][c];
}
suma[u]++;numa[u].push_back(id);
}
inline void insertb(int id,int x)
{
int u=0,c;
for(int i=29;i>=0;i--)
{
sumb[u]++;
c=(x>>i)&1;
if(!trb[u][c])
trb[u][c]=++totb;
u=trb[u][c];
}
sumb[u]++;numb[u].push_back(id);
}
inline void prework()
{
for(int i=0;i<=tota;i++)
{
tra[i][0]=tra[i][1]=0;
suma[i]=0;numa[i].clear();
}
for(int i=0;i<=totb;i++)
{
trb[i][0]=trb[i][1]=0;
sumb[i]=0,numb[i].clear();
}
scanf("%d",&n);
tota=0;totb=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
inserta(i,a[i]);ina[i]=true;
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
insertb(i,b[i]);inb[i]=true;
}
}
inline int finda(int x)
{
int u=0,c,tmp=0;
for(int i=29;i>=0;i--)
{
c=(x>>i)&1;
if(!tra[u][c])
u=tra[u][c^1];
else
u=tra[u][c];
}
it=numa[u].end();--it;
return (*it);
}
inline int findb(int x)
{
int u=0,c;
for(int i=29;i>=0;i--)
{
c=(x>>i)&1;
if(!trb[u][c])
u=trb[u][c^1];
else
u=trb[u][c];
}
it=numb[u].end();--it;
return (*it);
}
inline void dela(int x)
{
int u=0,c,last;suma[u]--;
for(int i=29;i>=0;i--)
{
c=(x>>i)&1;last=u;
u=tra[u][c];
suma[u]--;
if(suma[u]==0)
tra[last][c]=0;
}
it=numa[u].end();--it;
numa[u].erase(it);
}
inline void delb(int x)
{
int u=0,c,last;sumb[u]--;
for(int i=29;i>=0;i--)
{
c=(x>>i)&1;last=u;
u=trb[u][c];
sumb[u]--;
if(sumb[u]==0)
trb[last][c]=0;
}
it=numb[u].end();--it;
numb[u].erase(it);
}
inline void dfs(int k)
{
int id;
if(k&1)
{
id=finda(b[q[k-1]]);
if(id==q[k-2] && k-2>0)
{
delb(b[q[k-1]]);
dela(a[id]);
inb[q[k-1]]=false;
ina[id]=false;
ans[++ans[0]]=b[q[k-1]]^a[id];
k-=2;sum--;
}
else
{
w[k]=b[q[k-1]]^a[id];
q[k]=id;k++;
}
if(sum>0)
dfs(k);
}
else
{
id=findb(a[q[k-1]]);
if(id==q[k-2] && k-2>0)
{
dela(a[q[k-1]]);
delb(b[id]);
ina[q[k-1]]=false;
inb[id]=false;
ans[++ans[0]]=a[q[k-1]]^b[id];
k-=2;sum--;
}
else
{
w[k]=a[q[k-1]]^b[id];
q[k]=id;k++;
}
if(sum>0)
dfs(k);
}
}
inline void mainwork()
{
ans[0]=0;sum=n;
for(int i=1;i<=n;i++)
if(ina[i])
{
q[1]=i;
dfs(2);
}
}
inline void print()
{
sort(ans+1,ans+1+ans[0]);
for(int i=1;i<=n;i++)
printf("%d%c",ans[i],(i==n)?'\n':' ');
}
int main()
{
mi[0]=1;
for(int i=1;i<=29;i++)
mi[i]=mi[i-1]*2;
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++)
{
prework();
mainwork();
print();
}
return 0;
}