题解:P7362 [eJOI 2020] XOR Sort (Day2)

给点不一样的思路。

题意

给定一个长为 \(n\) 的序列 \(a\)。有一种操作是将相邻两个数的异或值赋给其中一个。要求用不超过 \(4\times10^4\) 次操作使序列满足:

  1. 单调递增(满足每个数只出现一次)
  2. 单调不减

思路

\(S=1\)

众所周知,我们可以用 \(3\) 次异或操作模拟一次交换。由于题目只允许相邻两数异或,我们也只能做到相邻两数交换。

因为 \(n\le 200\),所以我们使用 \(O(n^2)\) 的排序算法,于是考虑冒泡排序,操作次数 \(\mathcal O(n^2)\)。但是发现只能获得 \(50\) 分。考虑什么时候操作次数最多,发现是序列单调递减的时候。于是如果我们的操作次数 \(>4\times10^4\),就对相邻两个数都进行一次异或,再重新跑冒泡排序。但这样会出现一个问题,就是可能出现相同的数了。于是我们再对随机 \(O(n)\) 个位置进行操作即可。正确率很高。

\(S=2\)

由于这一部分只要求单调不减,所以我们考虑尽量把每个数变为 \(0\)。我们期望最后得到一个这个序列的线性基和若干个前缀 \(0\)。于是可以从高位到低位构建线性基,把每位上的 \(1\) 消到只剩最多一个,然后放到序列最后,并且在之后的操作中不去考虑这个数。

具体方法是:若当前位上 \(a_i\)\(1\),而 \(a_{i+1}\)\(0\),做一次 \(a_{i+1}\to a_i\oplus a_{i+1}\)。然后若当前位上 \(a_i\)\(1\)\(a_{i+1}\) 一定也为 \(1\),做一次 \(a_i\to a_i\oplus a_{i+1}\)。最大操作次数为 \(2n\log V\),可以通过。

代码

#include <bits/stdc++.h>
using namespace std;
constexpr int N=1e5+5;
mt19937 f(chrono::steady_clock::now().time_since_epoch().count());
int rd(int l,int r){return f()%(r-l+1)+l;}
int n,s,a[N],tmp[N];
vector<pair<int,int>>ans;
void Swap(int i,int j){
    if(i==j)return;
    ans.push_back({i,j});
    ans.push_back({j,i});
    ans.push_back({i,j});
    swap(tmp[i],tmp[j]);
}
signed main(){
    clock_t _st=clock();
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>s;
    for(int i=1;i<=n;i++)cin>>a[i];
    if(s==1){
        memcpy(tmp+1,a+1,n<<2);
        for(int i=1;i<=n;i++)for(int j=n;j>i;j--)if(tmp[j]<tmp[j-1])Swap(j,j-1);
        if(ans.size()>40000){
            ans.clear();
            memcpy(tmp+1,a+1,n<<2);
            for(int i=1;i<n;i++)ans.push_back({i,i+1}),tmp[i]^=tmp[i+1];
            for(int i=1;i<=n*2;i++){
                int x=rd(1,n-1);
                ans.push_back({x,x+1}),tmp[x]^=tmp[x+1];
            }
            for(int i=1;i<=n;i++)for(int j=n;j>i;j--)if(tmp[j]<tmp[j-1])Swap(j,j-1);
        }
    }else{
        for(int i=19;i>=0;i--){
            for(int j=1;j<n;j++){
                if((a[j]>>i&1)&&!(a[j+1]>>i&1)){
                    ans.push_back({j+1,j});
                    a[j+1]^=a[j];
                }
                if((a[j]>>i&1)&&(a[j+1]>>i&1)){
                    ans.push_back({j,j+1});
                    a[j]^=a[j+1];
                }
            }
            if(a[n]>>i&1)n--;
        }
    }
    cout<<ans.size()<<'\n';
    for(pair<int,int>x:ans)cout<<x.first<<' '<<x.second<<'\n';
    clock_t _ed=clock();
    cerr<<(_ed-_st)*1.0/CLOCKS_PER_SEC<<'\n';
    return 0;
}
posted @ 2026-03-05 17:30  Redolent  阅读(0)  评论(0)    收藏  举报