*题解:P16325 【MX-J29-T4】XOR and Swap
解析
注意到操作次数大概是二倍长度,且操作可逆,故考虑将 \(p\) 和 \(q\) 变成同一个排列,答案即为对 \(p\) 的操作拼上逆序的对 \(q\) 的操作。
不妨令递增的排列 \(a\) 为 \(p,q\) 都要到达的排列,该排列有 \(a_i = i\)。
对于 \(p_j\),假设其目标位置为 \(i\)。若要交换 \(i,j\),则对于原式 \(p_i \oplus p_j\le i\oplus j\),由于 \(p_j = i\),所以可以对调 \(p_j\) 和 \(i\) 变成 \(p_i \oplus i\le p_j\oplus j\)。
故每次贪心地取不在正确位置上的 \(p_j\oplus j\) 最大的 \(p_j\) 进行操作即可,这个过程可以用优先队列来维护。
时间复杂度 \(O(2^n\log 2^n) = O(n2^n)\)。
想到了的部分
将问题转化为 \(p,q\) 分别变成递增的排列。
将式子转化成 \(p_i \oplus i\le p_j\oplus j\),但是想到的转化方法是没有附加条件直接移项,是错误的。
没想到的部分
由 \(p_j = i\) 推导出 \(p_i \oplus i\le p_j\oplus j\)。
踩到的坑
试图对异或不等式进行移项。
没想到的原因
发现踩坑后没有思考能否换一种转化方法,于是不知道如何处理式子。没有考虑等量代换。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1.1e6 + 5,M = 6e3 + 5,K = 10000 + 5,mod = (int)1e9 + 7;
int p[N],q[N];
int n;
struct cmp{
bool operator ()(pii a,pii b){
return (a.first ^ a.second) < (b.first ^ b.second);
}
};
void sol(int *a,vector<pii> &res){
priority_queue<pii,vector<pii>,cmp> pq;
for(int i=0;i<n;i++){
if(i != a[i])
pq.push({i,a[i]});
}
while(!pq.empty()){
pii tp = pq.top();
pq.pop();
int pos = tp.first,num = tp.second;
if(a[pos] != num) continue;
res.push_back({pos,num});
swap(a[pos],a[num]);
if(pos != a[pos])
pq.push({pos,a[pos]});
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
cin>>n;
n = 1 << n;
for(int i=0;i<n;i++){
cin>>p[i];
}
for(int i=0;i<n;i++){
cin>>q[i];
}
vector<pii> res1,res2;
sol(p,res1);
sol(q,res2);
cout<<res1.size() + res2.size()<<'\n';
for(int i=0;i<res1.size();i++){
cout<<res1[i].first<<" "<<res1[i].second<<'\n';
}
for(int i=(int)res2.size() - 1;i>=0;i--){
cout<<res2[i].first<<" "<<res2[i].second<<'\n';
}
return 0;
}

浙公网安备 33010602011771号