[THUPC2022 初赛] 造计算机

发布时间 2023-08-18 16:59:08作者: -白简-

题目传送门

更好的阅读体验

思路

结论:如果序列原先就合法,答案为 \(0\);否则,最多使用两个寄存器。

我们对 \(i \rightarrow a_i\) 建边得到若干个环,我们单独考虑一个环如何操作。

对于一个长度为 \(4\) 的数列,再包含两个寄存器,设两个寄存器的值分别为 \(x,y\)

显然 \(4,1,3\) 组成了一个环,我们对其进行一些操作,使得他们回到他们想要到达的位置,即箭头指向的位置。

我们记 \(\operatorname{pos}_i\) 表示值 \(i\) 所在的位置,把 \(4\) 看作环的起点,那么把这个环拆成一条链就是 \(4,3,1\),下文称之为链。

首先我们将值 \(4\) 与值 \(5\) 交换位置,在此基础上再将值 \(3\) 与刚刚换到第五个位置的值 \(4\) 交换位置,除了链的最后一个元素外,剩下的元素按照上面的方式依次与第一个寄存器进行交换。

这样,我们链的第 \(1 \sim n-2\) 个元素都达到了自己的目标位置(他们指向的位置就是目标位置)。只剩下原本链的最后一个元素和倒数第二个元素没有达到目标位置。

我们寄存器原本的值 \(x\) 放在了链的第一个位置,链的倒数第二个元素放在了第一个寄存器的位置。链的最后一个元素仍在其原位置。

考虑如何处理剩下这两个元素。我们设链的倒数第二个值为 \(a\),最后一个值为 \(b\)

我们考虑把值 \(b\) 与值 \(y\) 交换,现在 \(y\) 在链的末尾,\(b\) 在第二个寄存器;

再考虑把刚刚换到链的末尾的值 \(y\) 与第一个寄存器的值进行交换,即将值 \(y\) 与值 \(a\) 交换,现在 \(a\) 在链的末尾(达到了目标位置),\(y\) 在第一个寄存器;

再考虑将第二个寄存器的值与链首的值进行交换,即把 \(b\)\(x\) 进行交换,\(b\) 达到了其目标位置。

到这里,前面的内存单元就已经合法了。我们再考虑一下两个寄存器顺序是否合法就可以了。

Code

#include <bits/stdc++.h>

using namespace std;

const int N = 100500;

int n,m;

int a[N];

vector< pair<int,int> > ans;
deque<int> st;

bool vis[N];

void dfs(int x) {
    vis[x] = 1;
    st.push_back(x);

    if(!vis[a[x]])
        dfs(a[x]);
    
    return ;
}


int main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1;i <= n; i++) 
        cin >> a[i];
    
    a[n + 1] = n + 1;
    a[n + 2] = n + 2;
    
    for(int i = 1;i <= n; i++) {
        if(!vis[i] && i != a[i]) {
            st.clear();
            
            dfs(i);

            m = 2;

            for(auto const &it : st) {
                if(it == st.back())
                    break;

                ans.emplace_back(it,n + 1);
                swap(a[it],a[n + 1]);
            }
            
            ans.emplace_back(st.back(),n + 2);
            swap(a[st.back()],a[n + 2]);

            ans.emplace_back(st.back(),n + 1);
            swap(a[st.back()],a[n + 1]);

            ans.emplace_back(st.front(),n + 2);
            swap(a[st.front()],a[n + 2]);
        }
    }

    if(a[n + 1] != n + 1) 
        ans.emplace_back(n + 1,n + 2);
    
    cout << m << " " << ans.size() << "\n";
    for(auto const &it : ans) 
        cout << it.first << " " << it.second << "\n";
    
    return 0;
}