QOJ 1435

感觉还是挺好玩的一个题目的。

首先显然猜想如果 \(inv\equiv \frac{n(n-1)}{2}\pmod 2\) 才有解。否则尝试构造。

如果现在存在 \(i\neq pos_i\),我们可以操作这个数并且用完他的所有余额让他回到应该在的位置上。这个其实就是先操作 \((i,j)\)\(j\) 余额没有用完)再操作 \((i,a_i)\)

否则现在已经排好序了,有几个位置没有被操作完。容易发现没有用完余额的数一定和用完的数全部都操作过了,我们单独把他们拎出来就可以了。不失一般性设为 \(1\sim x\)

容易发现 \(0\equiv \frac{x(x-1)}{2}\pmod 2\),因此 \(4\mid x\)。因为 \(2\mid 4\),我们可以 \(2\) 个数一组的操作(\(1\) 个数一组会有后效性)。具体的,我们对于 \(1\sim 4\) 操作 \((1,2),(1,3),(2,4),(1,4),(2,3)\) 即可,然后现在是 4 3 这种样式的,又变成了上面的问题。

因此一直递归即可。

更新:为什么你交替弄是对的?因为我们最后一定会归纳到一个 \(0\)\(4\) 个没有被操作完的 \(2\) 种操作。如果是 \(4\),显然可以做完。特别的,代码里面的 v.size() 可能是 \(1\),但是其他数都和他操作了,所以没有问题。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int N = 1e3+3;

int n,a[N],p[N],al[N];

void swp(int x,int y){
	swap(a[p[x]],a[p[y]]);
	swap(p[x],p[y]);
	cout<<x<<" "<<y<<endl;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);

	cin>>n;
	for (int i=1; i<=n; i++){
		cin>>a[i];
		p[a[i]]=i;
	}
	int cnt=0;
	for (int i=1; i<=n; i++){
		for (int j=i+1; j<=n; j++) cnt+=(a[i]>a[j]);
	}
	if (cnt%2!=(n*(n-1)/2)%2){
		cout<<"no\n";
		return 0;
	}
	while (true){
		int x=0;
		for (int i=1; i<=n; i++){
			if (p[i]!=i){
				x=i;
				break;
			}
		}
		if (x){
			al[x]=1;
			for (int i=1; i<=n; i++){
				if (!al[i] && x!=p[i]){
					swp(x,i);
				}
			}
			swp(x,a[x]);
		}
		else{
			vector<int> v;
			for (int i=1; i<=n; i++){
				if (!al[i]) v.push_back(i);
			}
			if (v.size()<4) return 0;
			al[v[0]]=al[v[1]]=1;
			for (int i=4; i<v.size(); i++){
				swp(v[0],v[i]);
			}
			for (int i=v.size()-1; i>=4; i--){
				swp(v[1],v[i]);
			}
			swp(v[0],v[1]);
			swp(v[0],v[2]);
			swp(v[1],v[3]);
			swp(v[0],v[3]);
			swp(v[1],v[2]);
		}
	}
	return 0;
}
posted @ 2024-12-04 15:22  SFlyer  阅读(28)  评论(0)    收藏  举报