Codeforces Round #252 (Div. 2) D. Valera and Swaps(神奇的置换群)(好题)

D. Valera and Swaps
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

permutation p of length n is a sequence of distinct integers p1, p2, ..., pn (1 ≤ pi ≤ n). A permutation is an identity permutation, if for any i the following equation holds pi = i.

swap (i, j) is the operation that swaps elements pi and pj in the permutation. Let's assume that f(p) is the minimum number of swaps that you need to make the permutation p an identity permutation.

Valera wonders, how he can transform permutation p into any permutation q, such that f(q) = m, using the minimum number of swaps. Help him do that.

Input

The first line contains integer n (1 ≤ n ≤ 3000) — the length of permutation p. The second line contains n distinct integersp1, p2, ..., pn (1 ≤ pi ≤ n) — Valera's initial permutation. The last line contains integer m (0 ≤ m < n).

Output

In the first line, print integer k — the minimum number of swaps.

In the second line, print 2k integers x1, x2, ..., x2k — the description of the swap sequence. The printed numbers show that you need to consecutively make swaps (x1, x2)(x3, x4), ..., (x2k - 1, x2k).

If there are multiple sequence swaps of the minimum length, print the lexicographically minimum one.

Sample test(s)
input
5
1 2 3 4 5
2
output
2
1 2 1 3 
input
5
2 1 4 5 3
2
output
1
1 2 
Note

Sequence x1, x2, ..., xs is lexicographically smaller than sequence y1, y2, ..., ys, if there is such integer r (1 ≤ r ≤ s), thatx1 = y1, x2 = y2, ..., xr - 1 = yr - 1 and xr < yr.





大致题意:n的排列(n<3000) 输出字典序最小的两两元素交换方案,使交换后的排列最少交换m次可以变成“原始排列pi = i”


思路:想了好久想不到“一个排列最少经过几次交换可以变成原始排列”这个问题,如果仅仅交换相邻元素的话就和逆序数有关了再用线段树乱搞搞就好了

然后得知有个知识叫“置换群”--http://www.cnblogs.com/buptLizer/archive/2011/11/15/2249551.html


然后需要理解这几点就可以做题辣:

1.一个大小为L的置换群里面的元素至少且必能互换L-1次得到原始排列

2.分别任取两个置换群的元素交换一次将合并成一个置换群(这个是显然正确的)

3.在一个置换群里面任取两个元素交换一次将分裂成两个置换群(这个证明不是很显然)



接下来,做这题就是暴力了= =,用2来暴力减少置换群,用1来暴力增加置换群就ok辣




#include <iostream>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <cstdio>
#include <ctime>
#include <bitset>
#include <algorithm>
#define SZ(x) ((int)(x).size())
#define ALL(v) (v).begin(), (v).end()
#define foreach(i, v) for (__typeof((v).begin()) i = (v).begin(); i != (v).end(); ++ i)
#define REP(i,n) for ( int i=1; i<=int(n); i++ )
using namespace std;
typedef long long ll;

const int N = 3000+100;
const int inf = 0x3f3f3f3f;
int a[N];
int fa[N];
int pos[N];
int getf(int x){
        return x == fa[x] ? x:fa[x] = getf(fa[x]);
}
void dfs(int u,int pa){
        
        if(pa != -1 && getf(a[u]) == getf(pa)) return;
        int v = a[u];
        fa[v] = getf(u);
        dfs(v,u);
}
pair<int,int> ans;
typedef pair<int,int> pii;
void solve(int u,int pa,int rt){
        if(pa != -1 && getf(a[u]) == getf(pa)) return ;
        int v = a[u];
        if(v == u) return ;
        if(ans > pii(rt,v)) ans = pii(rt,v);
        fa[v] = getf(u);
        solve(v,u,rt);
}
int cnt[N];
int main(){
        int n;
        cin>>n;
        REP(i,n)fa[i] = i;
        REP(i,n) scanf("%d",&a[i]),pos[a[i]] = i;
        int m;
        cin>>m;
        REP(i,n) if(getf(i) == i) dfs(i,-1);
        REP(i,n) cnt[getf(i)]++;
        int cur = 0;
        REP(i,n) if(cnt[i]) cur += cnt[i]-1;
        int nd = cur-m;
        printf("%d\n",abs(nd));
        if(nd > 0){
                while(nd--){
                         ans = pii(inf,inf);
                         REP(i,n) fa[i] = i;
                         REP(i,n) if(getf(i) == i) solve(i,-1,i);
                         printf("%d %d ",ans.first,ans.second);
                         swap(a[ans.first],a[ans.second]);
                }
        }
        else {
                nd = -nd;
                cnt[getf(1)] = 0;
                for(int p = 2;p <= n && nd;p++){
                        if(cnt[getf(p)]) {
                                printf("%d %d ",1,p);
                                nd--;
                                cnt[getf(p)] = 0;
                        }
                }
        }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值