QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: Suffix_Sum

Posted at: 2026-07-02 17:13:45

Last updated: 2026-07-02 17:27:20

Back to Problem

New Editorial for Problem #5033

下文中的 $add(i,j)$ 表示做一次操作。

首先看保证 $b_1=n,b_n=1$,$\forall 1 < i < n ,b_i=i$ 时怎么做,也就是交换 $n$ 和 $1$。如果有 $n=2^k$ 那就直接不停操作 $add(n,1)$ 就做完了;如果 $n \not = 2^k$ ,我们仿照上述思路,先将 $a_n$ 凑成 $2^k$,再把所有其他操作过的数还原回去。

我们记 $a_n$ 的当前值为 $x$,那么我们每次找出 $x$ 的 $lowbit$,记位 $lbt$。如果 $x=lbt$ 说明凑到了 $2^k$;如果 $x\not= lbt$ 我们就找到 $a_{2\times lbt}=2\times lbt$ (显然 $2\times lbt < n$ ) 并操作一次 $add(2\times lbt,n)$,可以使得 $x+=lbt$。

这样我们就使得 $x$ 凑到了 $2^k$,我们要将 $x$ 变为 $1$,并把之前所有的影响消除。每次检查一下在之前操作内是否有操作过 $a_x$。如果操作过 $a_x$ 说明目前的 $a_x=x/2$ ,操作一次 $add(n,x)$ 即可使得 $a_x$ 还原 ;如果没有操作过 $a_x$ 那就不用管它,直接操作一次 $add(n,1)$。

如此我们就在 $2\times log(n)$ 次操作内交换了 $1$ 和 $n$,可以发现,上述过程具有推广性,可以直接在 $2 \times log(n)$ 的操作次数内交换 $1$ 和任意的 $x$。那么就变成了一个经典的问题,每次可以交换 $1$ 和 $x$,使得序列复原,需要几步,把置换环建出来,先将 $1$ 所在的置换环还原,之后若遍历到一个置换环就将 $1$ 交换入其中,并将这个置换环还原,对于一个环来说,若其环长为 $c$,那么还原这个置换环只需要 $c+1$ 步,由于随机排列的置换环个数为 $sqrt(n)$ 故操作总次数为 $2\times logn \times (n+sqrt(n))$,算出来操作次数大概是 $3.5e6$ 左右,由于 $2\times log n$ 的其中一个 $log$ 可以视为树状数组的 $log$,在随机数据下常数相当小,实际上操作次数大概是 $2.5e6$ 左右。

参考代码:

#include<bits/stdc++.h>
#include"seq.h"
using namespace std;
const int N=2e5+5;
int pos[N],realpos[N];
int a[N],b[N];
int vis[N];
int lowbit(int x){
    return x&(-x);
}
int cnt=0;
void swp(int key){//跟1交换 
    int posk=pos[key],pos1=pos[1];
    while(key!=lowbit(key)){
        int now=lowbit(key)*2;
        add(pos[now],posk);
        cnt++;
        vis[now]=1;
        key+=now/2;
    }
    while(key>1){
        if(vis[key])add(posk,pos[key]),vis[key]=0;
        else add(posk,pos1);
        cnt++;
        key>>=1;
    }
    swap(a[posk],a[pos1]);
    pos[a[posk]]=posk;
    pos[a[pos1]]=pos1;
}
void solve(){
    while(1){
        int pos1=pos[1];
        if(b[pos1]==1)break;
        swp(b[pos1]);
    }
}
void SEQ(int n,int M){
    for(int i=1;i<=n;i++)a[i]=i,pos[i]=i,b[i]=Get(i);
    answer(1);
    solve();
    for(int i=1;i<=n;i++)if(a[i]!=b[i]){
        swp(a[i]);
        solve();
    }
    assert(cnt<=2.5e6);
}

Comments

No comments yet.