【Codeforces 1168E】Xor Permutations
构造。
题目大意:
给定一个长度为 $2^n$ 的数列 $a_0,a_1,a_2,\ldots,a_{2^n-1}$。
要求找到两个长度为 $2^n$ 的 $0\sim 2^n-1$ 的排列 $p,q$,满足 $p_i$ 和 $q_i$ 的按位异或的结果为 $a_i$。
问是否有可能的方案,如果有则输出任意一种。
题解:
神仙构造题。
先判断无解。如果 $a$ 的异或和不为 $0$ 则无解。
我们考虑 $v_i$ 表示目前 $p_i$ 和 $q_i$ 的按位异或的结果。初始令 $p_i=q_i=i$,$v_i=0$。
我们依次检查每个位置,如果 $v_i\neq a_i$,则进行以下的操作:
- 令 $d=v_i\mathrm{\ xor\ }a_i$,令 $v_i$ 和 $v_{i+1}$ 都异或上 $d$。此时 $v_{i}$ 已经不等于 $p_i\mathrm{\ xor\ }q_i$ 了,$i+1$ 也类似。
- 执行
fix(i,i+1)
。
其中 fix(i,j)
就是通过交换的操作,将 $p_i\mathrm{\ xor\ }q_i$ 修正为 $v_i$,$j$ 也一样的修正。
修正过程中,fix(i,j)
始终保证 $p_i\mathrm{\ xor\ }q_i\mathrm{\ xor\ }v_i=p_j\mathrm{\ xor\ }q_j\mathrm{\ xor\ }v_j$,而其他的 $a$ 和 $v$ 相等。
这个 fix(i,j)
的操作步骤如下:
- 令 $d=q_i\mathrm{\ xor\ v_i}$,找到位置 $k$ 满足 $p_k=d$。
- 若 $k=i$,则已经满足条件,修正完毕。
- 若 $k=j$,则交换 $p_i$ 和 $p_j$,修正完毕。
- 否则交换 $p_k$ 和 $p_i$,交换 $q_k$ 和 $q_j$,然后执行
fix(k,j)
。
上面找 $p_k=d$ 的位置的时候,可以使用一个数组来记录 $p_i$ 对应的 $i$。
观察一下它还有哪些性质。我们发现 $j$ 是固定不变的,而 $p_i$ 也是不变的,因为每次都是将 $p_i$ 和 $p_k$ 交换后调用 fix(k,j)
。这有助于我们分析复杂度。
我们需要证明,一个 $p_k$ 不会被选中两次。
我们假设一次操作前的状态是 $(p_k,q_k,v_k)$,$(p_i,q_x,v_y)(1)$,$(p_j,q_y,v_j)(2)$。
对其操作后变为:$(p_k,q_x,v_y)$,$(p_i,q_y,v_k)$,$(p_j,q_k,v_j)$。
然后又进行了若干递归以后,变成:$(p_k,q_x,v_y)$,$(p_i,q_m,v_n)$,$(p_j,q_n,v_j)$。
对其操作后变为:$(p_k,q_m,v_n)$,$(p_i,q_n,v_y)(3)$,$(p_j,q_x,v_j)(4)$。
那么:
由 $(1)$ 和 $(2)$ 可得:$q_y=p_i\mathrm{\ xor\ }q_x\mathrm{\ xor\ }v_y\mathrm{\ xor\ }p_j\mathrm{\ xor\ }v_j$。
由 $(3)$ 和 $(4)$ 可得:$q_n=p_i\mathrm{\ xor\ }q_x\mathrm{\ xor\ }v_y\mathrm{\ xor\ }p_j\mathrm{\ xor\ }v_j$。
所以 $q_y=q_n$。而 $y\neq n$,$q$ 又是一个排列,因此矛盾。
所以一个 $p_k$ 不会被选中两次。
那么在一次 fix(i,i+1)
中,只会进行 $O(2^n)$ 层的递归。而我们需要对 $2^n-1$ 个数 fix
一遍,因此时间复杂度 $O(4^n)$。
Code:
#include<cstdio>
#include<algorithm>
using std::swap;
const int N=4100;
int n,A[N],B[N],a[N],V[N],d,bel[N];
void fix(int x,int y){
int p=B[x]^V[x],t=bel[p];
if(t==x)return;
if(t==y){
swap(A[x],A[y]),swap(bel[A[x]],bel[A[y]]);return;
}
swap(bel[p],bel[A[x]]);
swap(A[t],A[x]),swap(B[t],B[y]),fix(t,y);
}
int main(){
scanf("%d",&n),n=1<<n;
for(int i=0;i<n;++i)scanf("%d",a+i),d^=a[i],A[i]=B[i]=bel[i]=i;
if(d)return puts("Fou"),0;
puts("Shi");
for(int i=0;i+1<n;++i)
if(V[i]!=a[i]){
int x=V[i]^a[i];
V[i]^=x,V[i+1]^=x;
fix(i,i+1);
}
for(int i=0;i<n;++i)printf("%d ",A[i]);
for(int i=0;i<n;++i)printf("%d ",B[i]);
return 0;
}