Description
nnn个点编号000~n−1n-1n−1,x,yx,yx,y之间边的权值为x⊕yx\oplus yx⊕y,其中⊕\oplus⊕为逻辑与操作,求该图的最小权匹配
Input
一个整数n(1≤n≤5⋅105)n(1\le n\le 5\cdot 10^5)n(1≤n≤5⋅105)
Output
输出nnn个整数pip_ipi表示iii与pip_ipi匹配
Sample Input
3
Sample Output
0 2 1
Solution1
从大往小找匹配,尽量把高位的111与上000,故把nnn个数插入字典树后,每次找和当前位不同的数字即可,时间复杂度O(nlogn)O(nlogn)O(nlogn)
Code1
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 500005
int chd[maxn*18][2],nn,num[maxn*18];
void init()
{
nn=1;
memset(chd[0],-1,sizeof(chd[0]));
}
void insert(int x,int sta)
{
int p=0;
for(int i=17;i>=0;i--)
{
int t=(x>>i)&1;
if(chd[p][t]==-1)
{
chd[p][t]=nn;
memset(chd[nn],-1,sizeof(chd[nn]));
nn++;
}
p=chd[p][t];
num[p]+=sta;
}
}
int find(int x)
{
int p=0;
int ret=0;
for(int i=17;i>=0;i--)
{
int t=((x>>i)&1)^1;
if(num[chd[p][t]]==0)t^=1;
p=chd[p][t];
ret<<=1;
ret|=t;
}
return ret;
}
int ans[maxn],vis[maxn];
int main()
{
int n;
scanf("%d",&n);
init();
for(int i=0;i<n;i++)insert(i,1);
memset(vis,0,sizeof(vis));
for(int i=n-1;i>=0;i--)
if(!vis[i])
{
insert(i,-1);
int j=find(i);
vis[i]=vis[j]=1;
ans[i]=j,ans[j]=i;
insert(j,-1);
}
for(int i=0;i<n;i++)printf("%d%c",ans[i],i==n-1?'\n':' ');
return 0;
}
Solution2
若n=2mn=2^mn=2m,那么把iii和n−1−in-1-in−1−i匹配即可使得答案为000
如果n>2mn>2^mn>2m,注意到2m,2m+1,...,2m+n−2m−12^m,2^m+1,...,2^m+n-2^m-12m,2m+1,...,2m+n−2m−1与2m−1,2m−2,...,2m−(n−2m)2^m-1,2^m-2,...,2^m-(n-2^m)2m−1,2m−2,...,2m−(n−2m)每一位二进制位都不同,故此时可以处理掉后2⋅(n−2m)2\cdot (n-2^m)2⋅(n−2m)个数字,之后递归处理即可,时间复杂度为O(n)O(n)O(n)
Code2
#include<cstdio>
using namespace std;
#define maxn 500005
int n,ans[maxn];
void Solve(int n)
{
int m=1;
while((m<<1)<=n)m<<=1;
if(n==m)
{
for(int i=0;i<n;i++)ans[i]=n-1-i;
return ;
}
for(int i=m;i<n;i++)ans[i]=2*m-1-i,ans[2*m-1-i]=i;
n-=2*(n-m);
Solve(n);
}
int main()
{
scanf("%d",&n);
Solve(n);
for(int i=0;i<n;i++)printf("%d%c",ans[i],i==n-1?'\n':' ');
return 0;
}