EJOI2018 循环排序 #LOJ2018 构造题

题目链接 ovo o v o
这个题 有点小恶心啊… 各种要判的东西都没判

先搬 Doe D o e 的题解…
这里写图片描述
这里写图片描述
这里写图片描述

如果看懂了把若干个环两次排序解决的方法 接下来的就比较简单了

首先判 1 − 1 错位个数大于操作下标数即无解‘
然后判 0 0 错位个数为0即答案为 0 0

我们对于离散后的每一个值开个vector 把每个元素最后应该到的位置的下标存进去,然后 dfs d f s 把这若干个环存下来

然后又到了特判时间
如果可操作次数减去不在原位置的数只剩下 1 1 <script type="math/tex" id="MathJax-Element-595">1</script> 就直接暴力拆所有环就够了…

剩下的情况 如果环的个数大于可操作次数

那就暴力拆环拆到环的个数等于可操作次数

然后再用那个两次排序的方法解决就好了 实现起来有点恶心看代码把

#include<bits/stdc++.h>

#define pb push_back

using namespace std;

const int N = 2e5 + 10;

vector<int> want[N], cir[N];
int n, m, num, cnt;
int a[N], tmp[N], b[N];
int fa[N];

void dfs(int u) {
    while(!want[u].empty()) {
        int v = want[u].back();
        want[u].pop_back();
        dfs(a[v]);
        cir[cnt].pb(v);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("2818.in", "r", stdin);
    freopen("2818.out", "w", stdout);
#endif
    int flag = 0, res = 0;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++ i) 
        scanf("%d", &a[i]), tmp[i] = a[i];
    sort(tmp + 1, tmp + n + 1);
    num = unique(tmp + 1, tmp + n + 1) - (tmp + 1);
    for(int i = 1; i <= n; ++ i)
        b[i] = a[i] = lower_bound(tmp + 1, tmp + num + 1, a[i]) - tmp;
    sort(b + 1, b + n + 1);
    for(int i = 1; i <= n; ++ i)
        if(a[i] != b[i])
            want[b[i]].pb(i), -- m, flag = 1;
    if(!flag) return printf("0"), 0;
    if(m < 0) return printf("-1"), 0;
    for(int i = 1; i <= num; ++ i)
        if(!want[i].empty()) {
            ++ cnt; dfs(i);
        }
    if(cnt == 1) {
        int sz = cir[1].size();
        printf("1\n%d\n", sz);
        for(int j = sz - 1; ~ j; -- j)
            printf("%d ", cir[1][j]);
        return 0;
    }
    if(m == 0) {
        printf("%d", cnt);
        for(int i = 1; i <= cnt; ++ i) {
            int sz = cir[i].size();
            printf("\n%d\n", sz);
            for(int j = sz - 1; ~ j; -- j)
                printf("%d ", cir[i][j]);
        }
        return 0;
    }
    m = min(m, cnt);
    if(m > 1) printf("%d\n", cnt - m + 2);
    else printf("%d\n", cnt);
    for(int i = cnt; i > m; -- i) {
        int sz = cir[i].size();
        printf("%d\n", sz);
        for(int j = sz - 1; ~ j; -- j)
            printf("%d ", cir[i][j]);
        puts("");
    }   
    for(int i = 1; i <= m; ++ i)
        res += cir[i].size();
    printf("%d\n", res);
    for(int i = 1; i <= m; ++ i)
        for(int j = cir[i].size() - 1; ~ j; -- j)
            printf("%d ", cir[i][j]);
    if(m > 1) {
        printf("\n%d\n", m);
        for(int i = m; i > 0; -- i) 
            printf("%d ", cir[i][cir[i].size() - 1]);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值