题意
有
n
n
n 个二元组
(
a
i
,
b
i
)
(a_i,b_i)
(ai,bi),二元组可以翻转。现在要将这些二元组接成一个环。连接部分的漂亮度是
l
o
w
b
i
t
x
x
o
r
y
lowbit_{x~xor~y}
lowbitx xor y(
x
=
y
x=y
x=y 时漂亮度为
20
20
20)。环的漂亮度为连接部分的最小漂亮度,现在要最大化漂亮度,并输出连接顺序。(第
i
i
i 个二元组的编号为
2
i
−
1
2i-1
2i−1 和
2
i
2i
2i)
其中
a
i
,
b
i
<
2
20
,
n
≤
500000
a_i,b_i< 2^{20},n\leq 500000
ai,bi<220,n≤500000。
分析
先考虑答案大于等于
l
l
l 时的性质:
也就是任意一个连接处,
x
x
o
r
y
x ~ xor~y
x xor y 是
l
l
l 的倍数。
因此答案具有单调性。
考虑怎么检验答案。
x
x
x 和
y
y
y 能作为连接处,意味着
x
x
o
r
y
x~xor~y
x xor y 在模
2
l
2^l
2l 意义下相同,也就是
x
≡
y
(
m
o
d
2
l
)
x\equiv y~(mod~2^l)
x≡y (mod 2l)。
那么,能否连成一个环,其实等价于是否存在欧拉回路(这个可能需要思考一下)。判断欧拉回路存不存在很好判断。
然后找到答案之后再求一边欧拉回路就可以了。
复杂度
O
(
V
log
20
+
n
log
20
)
O(V\log20 + n\log 20)
O(Vlog20+nlog20)。
代码如下
#include <bits/stdc++.h>
using namespace std;
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
const int N = 2000005;
int n, A[N], B[N], ans[N], m, h[N], f[N], du[N], vis[N * 2], cnt = 1;
struct node{
int a, b, x, y, n;
}d[N * 2];
void cr(int a, int b, int x, int y){
d[++cnt].a = a; d[cnt].b = b; d[cnt].x = x; d[cnt].y = y; d[cnt].n = h[a]; h[a] = cnt;
}
int find(int x){
return x == f[x]? x: f[x] = find(f[x]);
}
int chk(int x){
int i, j, a, b, tot = 0;
for(i = 0; i <= (1 << 20); i++) du[i] = 0, f[i] = i;
for(i = 1; i <= n; i++){
a = A[i] & x; b = B[i] & x;
du[a]++; du[b]++;
if(find(a) != find(b)) f[f[b]] = f[a];
}
for(i = 0; i <= (1 << 20); i++){
if(!du[i]) continue;
if(du[i] % 2) return 0;
if(find(i) == i) tot++;
}
return tot == 1;
}
void dfs(int a, int id){
for(int &i = h[a]; i; i = d[i].n){//此处不加引用会使复杂度高达 n^2,加了引用类似于网络流中的当前弧优化,也就是实时改变 h 指针
if(vis[i]) continue;
vis[i] = vis[i ^ 1] = 1;
int b = d[i].b;
dfs(b, i);
}
ans[++m] = id;
}
void work(int x){
int i, j, a, b;
for(i = 1; i <= n; i++){
a = A[i] & x; b = B[i] & x;
cr(a, b, 2 * i - 1, 2 * i);
cr(b, a, 2 * i, 2 * i - 1);
}
dfs(a, 1);
}
int main(){
int i, j, k, a, b, l = 0, r = 20;
n = read();
for(i = 1; i <= n; i++) A[i] = read(), B[i] = read();
while(l < r){
int mid = l + r + 1 >> 1;
if(chk((1 << mid) - 1)) l = mid;
else r = mid - 1;
}
printf("%d\n", l);
work((1 << l) - 1);
for(i = m - 1; i >= 1; i--){
j = ans[i];
printf("%d %d ", d[j].x, d[j].y);
}
return 0;
}