题目:http://codeforces.com/contest/441/problem/D 对于这道题目,也只能用 长知识 来安慰自己了,数学知识面有点狭窄,一开始在往逆序数方面想,发现不行,后来又强行模拟去搞,发现有漏的情况,后来看了 巨巨博客发现了新知识
推荐题解:http://www.cnblogs.com/xiaohongmao/p/3777794.html
http://zh.wikipedia.org/wiki/%E7%BD%AE%E6%8D%A2%E7%BE%A4
会记住:
一个轮换内交换成正常顺序需要k-1次,k为轮换内元素的个数
两个轮换之间进行交换元素,可以把两个轮换合并成1个,总交换次数+1
一个轮换内部进行交换,可以把一个轮换拆分成两个,总交换次数-1
#include<iostream>
#include<cstdio>
#include<list>
#include<algorithm>
#include<cstring>
#include<string>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<cmath>
#include<memory.h>
#include<set>
#include<cctype>
#define ll long long
#define eps 1e-8
const int inf = 0xfffffff;
const ll INF = 1ll<<61;
using namespace std;
//vector<pair<int,int> > G;
//typedef pair<int,int > P;
//vector<pair<int,int> > ::iterator iter;
//
//map<ll,int >mp;
//map<ll,int >::iterator p;
vector <pair <int ,int > > G;
int n;
int m;
int nnum[3000 + 55];
int vis[3000 + 55];
int detal() {
memset(vis,0,sizeof(vis));
int cnt = 1;
int ans = 0;
for(int i=1;i<=n;i++) {
if(!vis[i]) {
vis[i] = cnt++;
int now = 0;
for(int j=nnum[i];!vis[j];j = nnum[j]) {
vis[j] = vis[i];
now++;
}
ans += now;
}
}
return ans;
}
void init() {
memset(nnum,0,sizeof(nnum));
G.clear();
}
bool input() {
while(scanf("%d",&n) == 1) {
for(int i=1;i<=n;i++)
scanf("%d",&nnum[i]);
scanf("%d",&m);
return false;
}
return true;
}
void cal() {
while(true) {
int now = detal();//原序列按照字典序排好 需要的交换步数
if(now == m)break;
if(now < m) {
for(int i=2;i<=n;i++) {
if(vis[i] != vis[1]) {
swap(nnum[1],nnum[i]);
G.push_back(make_pair(1,i));break;
}
}
}//两个轮换之间进行,可以把两个轮换给合并,按照定理+1,为了字典序跟1交换
else {
int mark;
for(int i=1;i<=n;i++) if(nnum[i] != i){mark = i;break;}
for(int j = mark + 1;j<=n;j++) {
if(vis[j] == vis[mark]) {
swap(nnum[mark],nnum[j]);
G.push_back(make_pair(mark,j));break;
}
}
}//一个轮换内进行,可以把一个轮换拆成两个,按照定理要-1
}
}
void output() {
printf("%d\n",G.size());
for(int i=0;i<G.size();i++)
printf("%d %d%c",G[i].first,G[i].second,i == G.size()?'\n':' ');
}
int main () {
while(true) {
init();
if(input())return 0;
cal();
output();
}
}