http://codeforces.com/problemset/problem/441/D

置换群的基本问题,一个轮换内交换成正常顺序需要k-1次,k为轮换内元素个数

两个轮换之间交换元素,可以把两个轮换合并成1个,总交换次数+1

一个轮换内部交换,可以把一个轮换拆分成两个,总交换次数-1

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std ;
int n,m ;
int nxt[3005],vis[3005] ;
vector <pair<int,int> > ans ;
int cal()
{
    memset(vis,0,sizeof(vis)) ;
    int st=1 ;
    int cnt=0 ;
    for(int i=1 ;i<=n ;i++)
        if(!vis[i])
        {
            vis[i]=st++ ;
            int res=1 ;
            for(int j=nxt[i] ;!vis[j] ;j=nxt[j])
            {
                vis[j]=vis[i] ;
                res++ ;
            }
            cnt+=(res-1) ;
        }
    return cnt ;
}
int main()
{
    scanf("%d",&n) ;
    for(int i=1 ;i<=n ;i++)
        scanf("%d",&nxt[i]) ;
    scanf("%d",&m) ;
    while(1)
    {
        int ret=cal() ;
        if(ret==m)break ;
        if(ret<m)//两个轮换换,可以把两个轮换合并成1个,ret+1 ,保证字典序最小,所以和位置1换 
        {
            for(int i=2 ;i<=n ;i++)
                if(vis[i]!=vis[1])
                {
                    swap(nxt[1],nxt[i]) ;
                    ans.push_back(make_pair(1,i)) ;
                    break ;
                }
        }
        else//一个轮换内换,可以把一个轮换拆成两个,ret-1 
        {
            int i,j ;
            for(i=1 ;i<=n ;i++)
                if(nxt[i]!=i)break ;
            for(int j=i+1 ;j<=n ;j++)
                if(vis[j]==vis[i])
                {
                    swap(nxt[i],nxt[j]) ;
                    ans.push_back(make_pair(i,j)) ;
                    break ;
                }
        }
    }
    printf("%d\n",ans.size()) ;
    for(int i=0 ;i<ans.size() ;i++)
        printf("%d %d ",ans[i].first,ans[i].second) ;
    return 0 ;
}
View Code