1 条题解

  • 0
    @ 2025-8-24 22:23:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar xlpg0713
    **

    搬运于2025-08-24 22:23:20,当前版本为作者最后更新于2024-06-18 10:21:14,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    给定长为 nn 的序列 aa,一次操作可以指定 l,rl,r,将 al,al+1ara_l,a_{l+1}\cdots a_r 中最大值与最小值调换位置,在 qq 次操作以内将 aa 变为另一指定序列。

    首先要发现这个东西是可逆的,意思是做两次相同的操作不会改变这个序列。也即:如果一个序列经过若干次操作后达到一个状态,将操作序列翻转可以还原到原序列。

    于是考虑找到一种操作方式将原序列排序,就解决了这个问题。

    至于排序方法,考虑进行一个类似归并的分治。问题在于如何合并两个已经有序的序列。

    如果有两个不相交的序列,可以用这种方式来排序:先将两个序列各自翻转,拼接成为一个下降序列,然后翻转回来。代价是 O(n)O(n)

    于是就可以对值域分治,过程形如这样:

    上半部分和下半部分是一个子问题。

    于是就可以实现排序,分治地做下去即可。

    分析一下复杂度,最外层的分治有 logn\log n 层,对值域分治聪明一点也是 logn\log n 层,合并两个不相交的序列,一层的代价总和是 O(n)O(n),于是需要 O(nlog2n)O(n\log^2n) 次操作,能看出来常数是挺小的,可以过。

    #include<iostream>
    #include<algorithm>
    #include<vector>
    const int N = 5005; int n, a[N], b[N]; 
    std::vector<std::pair<int, int>> rs, rs;
    void ad(int l, int r){rs.push_back({l, r}); std::swap(a[l], a[r]);}
    void mg(int l, int r){
        int f = 0, md = l, p1 = l, p2 = r;
        for(int i = l; i < r; i++) f |= (a[i] > a[i + 1]);
        if(f == 0) return; while(a[md] < a[md + 1]) md++;
        for(int i = l; i <= r; i++) b[i] = a[i];
        std::nth_element(b + l, b + md, b + r + 1);
        while(a[p1] <= b[md]) p1++; while(a[p2] > b[md]) p2--;
        for(int i = p1, j = md; i < j; i++, j--) ad(i, j);
        for(int i = md + 1, j = p2; i < j; i++, j--) ad(i, j);
        for(int i = p1, j = p2; i < j; i++, j--) ad(i, j);
        mg(l, md), mg(md + 1, r);
    }
    void divide(int l, int r){
        if(l == r) return; int md = l + r >> 1;
        divide(l, md), divide(md + 1, r), mg(l, r);
    }
    int main(){
        std::ios::sync_with_stdio(false);
        std::cin.tie(0),std::cout.tie(0);
        std::cin >> n; 
        for(int i = 1; i <= n; i++) std::cin >> a[i];
        divide(1, n); rs = rs; rs.clear();
        for(int i = 1; i <= n; i++) std::cin >> a[i];
        divide(1, n); std::reverse(rs.begin(), rs.end());
        for(auto x:rs) rs.push_back(x);
        std::cout << rs.size() << '\n';
        for(auto [l, r] : rs) std::cout << l << ' ' << r << '\n';
    }
    
    • 1

    信息

    ID
    5724
    时间
    2000ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者