QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: vme50

Posted at: 2026-02-26 07:13:52

Last updated: 2026-02-26 07:14:04

Back to Problem

赛时做法,与 std 略有不同。

令 $a_0=0$,$b_{a_i}=a_{i+1}$。接下来的下标与值均在 $\bmod (n+1)$ 意义下考虑。

定义 $G(b)$ 表示 $i$ 向 $b_i$ 连边得到的有向图。

考虑一次操作 $i,j,k,l$,我们相当于先交换 $b_{i-1},b_{k-1}$,再交换 $b_j,b_l$。最终的目标为 $\forall i,b_i=i+1$。

于是自然想到将一次操作分裂为两个二元组 $(a_{i-1},a_{k-1}),(a_j,a_l)$。最终相当于是依次考虑每个二元组 $(x,y)$,并交换 $b_x,b_y$。

$G(b)$ 初始为一个环,交换 $b_x,b_y$ 后 $G(b)$ 会变为两个环。而题目中给出的操作额外要求当访问完偶数个二元组之后 $G(b)$ 为一个环。

不妨先忽略这个限制(即不关心 $G(b)$ 的环数)。则问题变为每次可以交换 $b$ 中任意两个元素,用最少的步数变为 $\forall i,b_i=i+1$。这是经典问题,这里不再赘述。

于是我们得到了不考虑限制的情况下的一组解。考虑对其进行调整使得它满足限制。

设 $(x_i,y_i)$ 表示上述解中的第 $i$ 个二元组。

我们钦定 $(x_1,y_1)$ 不变。则交换 $b_{x_1},b_{y_1}$ 之后 $G(b)$ 变为两个环,设它们的点集分别为 $S,T$。

因为在访问所有二元组之后 $G(b)$ 为一个环,所以一定存在 $i\in [2,2t]$ 使得 $x_i\in S,y_i\in T$ 或 $x_i\in T,y_i\in S$。

我们找到满足上述条件的最小的 $i$。不断地尝试将 $(x_i,y_i)$ 与 $(x_{i-1},y_{i-1})$ 交换顺序,并令 $i\leftarrow i-1$,直到 $i=2$ 为止。

令原来的顺序为 $(x_{i-1},y_{i-1}),(x_i,y_i)$。交换的方法如下:

  • $\{x_i,y_i\}=\{x_{i-1},y_{i-1}\}$ 的情况不可能出现,否则一定不是最优解。

  • 若 $x_i,y_i,x_{i-1},y_{i-1}$ 互不相同,则变为 $(x_i,y_i),(x_{i-1},y_{i-1})$。

  • 否则不妨设 $x_i=x_{i-1}$,则变为 $(y_{i-1},y_i),(x_{i-1},y_{i-1})$。

可以发现,上述交换过程不会改变 $b$ 在施加所有操作后的最终状态。并且在上述过程结束时一定有 $x_2\in S,y_2\in T$ 或 $x_2\in T,y_2\in S$。因此访问完 $(x_1,y_1),(x_2,y_2)$ 后 $G(b)$ 为一个环。

归纳地进行类似的调整,最终可以使得整个序列满足条件。

构造出满足限制的操作序列之后将其转化成原题的输出还需要讨论一些细节,不过这是 trivial 的,请读者自行思考(

暴力实现的时间复杂度即为 $O(n^2)$,足以通过。

参考代码(代码中在序列末尾增加了一项,应该是没有必要的):

#include <bits/stdc++.h>
using namespace std;
#define N 2005
int n,tp,a[N],b[N],vs[N],o[N];struct Node {int x,y;}z[N];
void swp(int x)
{
    if(z[x].x==z[x-1].x) z[x].x=z[x-1].y;
    else if(z[x].x==z[x-1].y) z[x].x=z[x-1].x;
    else if(z[x].y==z[x-1].x) z[x].y=z[x-1].y;
    else if(z[x].y==z[x-1].y) z[x].y=z[x-1].x;
    swap(z[x],z[x-1]);
}
void get() {int nw=0;for(int i=b[0];i;i=b[i]) o[i]=++nw;}
int main()
{
    scanf("%d",&n);for(int i=1;i<=n;++i) scanf("%d",&a[i]);++n;a[n]=n;
    for(int i=0;i<=n;++i) b[(a[i]+1)%(n+1)]=a[(i+1)%(n+1)];
    for(int i=0;i<=n;++i) if(!vs[i])
    {    
        for(int j=b[i];j!=i;j=b[j])
            vs[j]=1,z[++tp]=(Node) {(i+n)%(n+1),(j+n)%(n+1)};vs[i]=1;
    }for(int i=0;i<=n;++i) b[a[i]]=a[(i+1)%(n+1)];
    for(int i=1,t;i<=tp;i+=2)
    {
        t=0;swap(b[z[i].x],b[z[i].y]);fill(vs,vs+n+1,0);
        for(int j=z[i].x;!vs[j];j=b[j]) vs[j]=1;
        for(int j=z[i].y;!vs[j];j=b[j]) vs[j]=2;
        for(int j=i+1;j<=tp;++j) if(vs[z[j].x]^vs[z[j].y]) {t=j;break;}
        while(t>i+1) swp(t--);swap(b[z[i+1].x],b[z[i+1].y]);
    }for(int i=0;i<=n;++i) b[a[i]]=a[(i+1)%(n+1)];printf("%d\n",tp/2);
    for(int i=1;i<=tp;i+=2)
    {
        get();swap(b[z[i].x],b[z[i].y]);swap(b[z[i+1].x],b[z[i+1].y]);
        if(o[z[i].x]>o[z[i].y]) swap(z[i].x,z[i].y);
        if(o[z[i+1].x]>o[z[i+1].y]) swap(z[i+1].x,z[i+1].y);
        while(o[z[i].x]>=o[z[i+1].x] || o[z[i].y]>=o[z[i+1].y])
        {
            swp(i+1);if(o[z[i].x]>o[z[i].y]) swap(z[i].x,z[i].y);
            if(o[z[i+1].x]>o[z[i+1].y]) swap(z[i+1].x,z[i+1].y);
        }printf("%d %d %d %d\n",o[z[i].x]+1,o[z[i+1].x],o[z[i].y]+1,o[z[i+1].y]);
    }return 0;
}

Comments

No comments yet.