题解 CF1427D Unshuffling a Deck

伞兵题,写了一下午。
开始想错了构造方法,写完才发现,然后又想和写了很久(

给出一个排列,每次可以把其划分成若干段,段内不变,段出现顺序反转,求构造方案使排列有序。

题目要求在 \(n\) 次内完成排序,这引导着我们想到一次排上一个,要是能够第一次把 \(1\) 放在最前面,第二次把 \(2\) 放在第二个直到第 \(n\) 次把 \(n\) 放在最后一个就好了。

但试了试发现这没法做到,那就退而求其次,不要求顺序,只要把这几个数放在一起就可以了,即第一次其中有一块是 \(1\),第二次有一块是 \(12\)\(21\),第三次有一块是 \(123\)\(321\),以此类推。

我一开始的想法是倒着来,因为要翻转嘛,那么从后往前排,但这样和直接从前往后没有什么区别。
既然是翻转,那么就想个办法把连续的数放前面或反转后放后面,这样是可以做到的。举个例子,\(3/15624 \to 1/562/43 \to 4/356/2/1 \to 1/2/3/564 \to 564321 \to \cdots\)

容易发现,只要枚举当前新放进去的数,把小于它的数前面或后面划开,并把这个数前面或后面划开就可以了。

在实现上,我写了个结构体,维护了一个能自由切割和拼接的数组,使代码更清晰。

如果在 CF 赛场上遇到这题恐怕要写很久,因为开始想了个错误的 构造写了半天,代码细节也有些多。

代码
#include <iostream>
#include <algorithm>
#include <vector>
struct twt {
    std::vector<int> a;
    twt() { a.clear(); }
    void push(int x) { a.push_back(x); }
    int find(int x) {
        int an = a.size() + 1;
        for (int i = 0; i < (int)a.size(); i++)
            if (a[i] == x) return i+1;
        return an;
    }
    twt sub(int l, int r) {
        twt an;
        for (int i = l; i <= r; i++) {
            if (i < 1 || i > (int)a.size()) return twt();
            an.push(a[i-1]);
        }
        return an;
    }
    twt operator + (twt y) {
        twt an = *this;
        an.a.insert(an.a.end(), y.a.begin(), y.a.end());
        return an;
    }
    bool fin() {
        for (int i = 0; i < (int)a.size(); i++)
            if (a[i] != i+1) return false;
        return true;
    }
    int size() { return a.size(); }
} now;
int n, x, ttt;
std::vector<int> pr, tmp;
std::vector<twt> dwd;
std::vector<std::vector<int> > ans;
std::ostream& operator << (std::ostream& out, twt x) {
    for (int i = 0; i < (int)x.a.size(); i++) out << x.a[i] << ' ';
    out << "#\n";
    return out;
}
int main() {
    std::cin >> n;
    for (int i = 1; i <= n; i++) std::cin >> x, now.push(x);
    
    if (now.a[0] != 1) { 
        pr.clear(); pr.push_back(now.find(1)-1), pr.push_back(n-now.find(1)+1), ans.push_back(pr); 
        now = now.sub(now.find(1), n) + now.sub(1, now.find(1)-1);
    }
    
    for (int i = 2; !now.fin() && i <= n; i++) {
        pr.clear(), tmp.clear(), dwd.clear();
        for (int j = 1; j < i; j++) tmp.push_back(now.find(j));
        tmp.push_back(now.find(i));
        if (now.a[0] == 1) tmp.push_back(0); else tmp.push_back(1);
        if (now.a[0] == 1) tmp.push_back(n); else tmp.push_back(n+1);
        std::sort(tmp.begin(), tmp.end());
        for (int j = 1; j < (int)tmp.size(); j++)
            if (tmp[j] != tmp[j-1]) pr.push_back(tmp[j] - tmp[j-1]);
        ans.push_back(pr);
        for (int i = 0, l = 1; i < (int)pr.size(); l += pr[i], i++)
            dwd.push_back(now.sub(l, l+pr[i]-1)); 
        now = twt();
        for (int i = (int)dwd.size()-1; i >= 0; i--)
            now = now + dwd[i];
    }
   
    std::cout << ans.size() << '\n';
    for (int i = 0; i < (int)ans.size(); i++, std::cout << '\n') {
        std::cout << ans[i].size() << ' ';
        for (int j = 0; j < (int)ans[i].size(); j++)
            std::cout << ans[i][j] << ' ';
    }
}
posted @ 2021-08-07 15:50  Acfboy  阅读(36)  评论(0编辑  收藏  举报