*题解: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;
}
posted @ 2026-09-21 11:40  yutar  阅读(8)  评论(0)    收藏  举报