跳到主要内容

Bitovi

· 阅读需 5 分钟

[COCI 2023/2024 #5] Bitovi​

题干

题目背景

译自 COCI 2023/2024 Contest #5 T2「Bitovi」

题目描述

哪个先出现,鸡还是蛋?作为一个百万富翁活上一百年好,还是贫穷中度过七天好?如何成为国际象棋大师?如何拉起百叶窗?如何通过期末考试?如何训练龙?这些都是一些有趣的问题,我们可以在竞赛结束后再去思考,但现在我们提出一个不那么有趣的计算机科学问题。

给定两组数集 AA 和 BB,大小均为 NN。每次操作,你可以从集合 AA 中选择一个任意元素,并改变其二进制表示中的一个任意数位。结果数不能是改变前集合 AA 中的元素。

例如,数字 55 的二进制是 010120101_2。通过一次操作,它可以变成 13=1101213=1101_2、1=000121=0001_2、7=011127=0111_2 或 4=010024=0100_2。

确定一系列操作,通过这些操作集合 AA 变得和集合 BB 相等。如果两个集合大小相同,并且集合 AA 中没有不属于集合 BB 的元素,则认为两个集合是相等的。

注意:操作的数量不必是最小的,但必须满足任务的限制。

输入格式

第一行包含一个整数 NN(1≤N≤2151 \le N \le 2^{15}),集合 AA 和 BB 的大小。

第二行包含 NN 个不同的整数 aia_i(0≤ai<2150 \le a_i < 2^{15}),表示集合 AA 的元素。

第二行包含 NN 个不同的整数 bib_i(0≤bi<2150 \le b_i < 2^{15}),表示集合 BB 的元素。

输出格式

第一行输出操作的数目,不超过 2192^{19}。

接下来若干行,每行输出 x,yx,y(0≤x,y<2150 \le x, y < 2^{15}),表示将集合 AA 中的 xx 修改为 yy。xx 和 yy 在二进制位中只能有一位不同。并且,必须满足 x∈Ax\in A,y∉Ay\not \in A。

输入输出样例 #1

输入 #1

3
0 1 2
1 2 3

输出 #1

2
1 3
0 1

输入输出样例 #2

输入 #2

3
4 8 31
0 4 8

输出 #2

5
31 30
30 28
28 24
24 16
16 0

输入输出样例 #3

输入 #3

5
0 1 2 4 5
7 6 5 3 2

输出 #3

9
1 3
3 7
0 1
1 0
2 6
0 2
7 3
5 7
4 5

说明/提示

样例解释 1

如果我们先操作 0 10\ 1,再操作 1 31\ 3,两次操作间,集合 AA 将有两个相同的元素,这是不被允许的。另一种可能的方案是 2 32\ 3,0 20\ 2。

样例解释 2

31=11111231=11111_2。按照从最低有效位到最高有效位修改,依次可以得到 30,28,24,1630,28,24,16 和 00。在所有的操作后,AA 和 BB 相同。

子任务

SubtaskPointsConstraints
110ai,bi≤214a_i,b_i \le 2^{14}
215N≤7N \le 7
330N≤27N \le 2^7
415无额外限制

分析​

首先我们先对整个序列进行一次"去重",将两个序列中相同的数先去掉,保证每个数需要操作至少一次

首先我们对每对去操作变化,每次就是一个log⁡\log的复杂度,但是如何处理重复的情况呢

假设现在存在一个变化情况xx->yy->zz但是yy已经存在,此时我们不妨先用那个已经存在的yy去操作

在最后,我们再执行xx->yy就行了,相当于我们把这个操作暂缓处理

时间复杂度O(nlogn)O(nlogn)


AC代码​

Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair< int,int >
#define QWQ return 0;
#define QAQ return

const int N=20;

set< int > a, b, c;
stack< pii > rep;
vector< pii > ans;

inline void ope( int x,int y )
{
if( a.find( y ) != a.end() )
{ rep.push( { x,y } ); QAQ; }
a.erase( x ); a.insert( y );
ans.push_back( { x,y } );
}

inline int lowbit( int x ){ QAQ x&-x; }

inline void turn( int x,int y )
{
while( x != y )
{
int diff=x^y; diff=lowbit( diff );
ope( x,x^diff );
x^=diff;
}
while( !rep.empty() )
{
auto [nx,ny]=rep.top();
rep.pop();
turn( nx,ny );
}
}

int main()
{
// freopen( "1.in","r",stdin );
// freopen( "1.out","w",stdout );
ios::sync_with_stdio( false );
cin.tie( nullptr ); cout.tie( nullptr );
int n;
cin>>n;
for( int i=1, x;i<=n;i++ )
cin>>x,
a.insert( x ), c.insert( x );
for( int i=1, x;i<=n;i++ )
cin>>x,
b.insert( x );
while( !b.empty() )
{
int y=*b.begin();
if( a.find( y ) != a.end() )
{ b.erase( y ); c.erase( y );
continue; }
int x=*c.begin();
turn( x,y );
b.erase( y ); c.erase( x );
}
cout<<ans.size()<<"\n";
for( auto &[x,y]:ans ) cout<<x<<" "<<y<<"\n";
QWQ
}