[loj2818][eJOI2018]循环排序——思维题

题目大意:

You are given an array of n n positive integers a1,a2,,an. You can perform the following operation any number of times: select several distinct indices i1,i2,,ik i 1 , i 2 , … , i k ( 1ijn 1 ≤ i j ≤ n ) and move the number standing at the position i1 i 1 to the position i2 i 2 , the number at the position i2 i 2 to the position i3 i 3 , …, the number at the position ik i k to the position i1 i 1 . In other words, the operation cyclically shifts elements: i1i2iki1 i 1 → i 2 → … i k → i 1 .
For example, if you have n=4 n = 4 , an array a1=10,a2=20,a3=30,a4=40 a 1 = 10 , a 2 = 20 , a 3 = 30 , a 4 = 40 , and you choose three indices i1=2 i 1 = 2 , i2=1 i 2 = 1 , i3=4 i 3 = 4 , then the resulting array would become a1=20,a2=40,a3=30,a4=10 a 1 = 20 , a 2 = 40 , a 3 = 30 , a 4 = 10 .
Your goal is to make the array sorted in non-decreasing order with the minimum number of operations. The additional constraint is that the sum of cycle lengths over all operations should be less than or equal to a number s s . If it’s impossible to sort the array while satisfying that constraint, your solution should report that as well.
Input
The first line of the input contains two integers n and s s (1n200000, 0s200000 0 ≤ s ≤ 200 000 )—the number of elements in the array and the upper bound on the sum of cycle lengths.
The next line contains n n integers a1,a2,,an—elements of the array ( 1ai109 1 ≤ a i ≤ 10 9 ).
Output
If it’s impossible to sort the array using cycles of total length not exceeding s s , print a single number “-1” (quotes for clarity).
Otherwise, print a single number q— the minimum number of operations required to sort the array.
On the next 2q 2 ⋅ q lines print descriptions of operations in the order they are applied to the array. The description of i i -th operation begins with a single line containing one integer k ( 1kn 1 ≤ k ≤ n )—the length of the cycle (that is, the number of selected indices). The next line should contain k k distinct integers i1,i2,,ik ( 1ijn 1 ≤ i j ≤ n )—the indices of the cycle.
The sum of lengths of these cycles should be less than or equal to s s , and the array should be sorted after applying these q operations.
If there are several possible answers with the optimal q q <script type="math/tex" id="MathJax-Element-2837">q</script>, print any of them.

思路:

考虑一个位置的数,如果它要去另外一个位置的话,直接移动到另外一个位置一定是最优的,因为如果移动到其它的地方之后又要移动回来。
所以最后就是一个有若干个环的有向图。操作次数最少的方法是所有的数全部移动一次,之后每个环内错位的数再移动一次。操作位置最少的方法是每个环单独移动。
这里求的是操作的次数最少,不难发现操作次数最少就有可能满足不了操作位置的限制,所以我们可以单独移动一些环,使得操作位置符合要求的情况下操作次数最少。
如果有相同的数的话他们是可以任意连边的,但是这样会增加环的个数,不难发现如果两个环内有相同的数字的话是可以合并成一个环的,用并查集维护。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;

using namespace std;

template<typename T>void read(T &_){
    T __=0,mul=1; char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')mul=-1;
        ch=getchar();
    }
    while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    _=__*mul;
}

void File(){
    freopen("gen.in","r",stdin);
    freopen("gen.out","w",stdout);
}

const int maxn=2e5+10;
int n,m,a[maxn],to[maxn],ans,cnt,tot,num[maxn];
pii b[maxn];
bool yes[maxn],vis[maxn];

int fa[maxn];
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}

void init(){
    read(n); read(m);
    REP(i,1,n)read(a[i]),b[i]=mk(a[i],i);
    sort(b+1,b+n+1);
    REP(i,1,n)if(a[i]==b[i].fi)yes[i]=1;
    REP(i,1,n)fa[i]=i;
    int p=1;
    REP(i,1,n){
        if(yes[b[i].se])continue;
        while(yes[p])++p;
        to[b[i].se]=p++;
        fa[find(b[i].se)]=find(to[b[i].se]);
    }
    REP(i,1,n){
        int l=i,r=i,las=0;
        while(b[r+1].fi==b[l].fi)++r;
        REP(j,l,r){
            if(yes[b[j].se])continue;
            if(!las){las=j;continue;}
            if(find(b[las].se)==find(b[j].se))continue;
            fa[find(b[las].se)]=find(b[j].se);
            swap(to[b[las].se],to[b[j].se]);
            las=j;
        }
        i=r;
    }
}

int Mincost(){return tot<=2 ? cnt : tot+cnt;}

int solve(int x){
    int qu[maxn],q=0,p=to[x];
    qu[++q]=x;
    while(p!=x)qu[++q]=p,p=to[p];
    printf("%d\n",q);
    REP(i,1,q)printf("%d ",qu[i]);
    putchar('\n');
    return q;
}

void work(){
    REP(i,1,n){
        if(yes[i])continue;
        ++cnt;
        if(!vis[find(i)]){
            num[++tot]=find(i);
            vis[find(i)]=1;
        }
    }
    if(cnt>m){puts("-1");return;}
    if((tot>2 ? cnt+tot : cnt)<=m)ans=2;
    else ans=2+cnt+tot-m;
    ans=min(ans,tot);
    printf("%d\n",ans);
    while((tot>1 ? cnt+tot : cnt)>m){
        int cost=solve(num[tot]);
        m-=cost; cnt-=cost; --tot;
    }
    if(tot==1)solve(num[tot]);
    else if(tot){
        int qu[maxn],q1[maxn],q=0,qq=0;
        while(tot){
            int p=to[num[tot]]; qu[++q]=num[tot]; q1[++qq]=num[tot];
            while(p!=num[tot])qu[++q]=p,p=to[p];
            --tot;
        }
        printf("%d\n",q);
        REP(i,1,q)printf("%d ",qu[i]);
        putchar('\n');
        printf("%d\n",qq);
        DREP(i,qq,1)printf("%d ",q1[i]);
        putchar('\n');
    }
} 
int main(){
    File();
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值