P7362 [eJOI 2020 Day2] XOR Sort
题意
给你一个长度为 (n) 的序列,每次操作可以将一个数异或上相邻的一个数,求将序列改为严格单调递增序列或严格单调不降序列的操作次数的操作序列。
注意,改为严格单调递增序列的数据 (nleq 200) 且每个数不相同,改为严格单调不降序列的数据 (nleq 1000)。
不要求操作次数最小,次数需不大于 (40000),元素大小小于 (2^{20})。
思路
严格单调递增序列的数据实测如果改了数列很容易造成数字相同然后挂掉的情况出现。反而严格单调不降的条件用异或容易满足且数据范围大。我们考虑对数据分治。
- 严格单调递增序列
我们考虑冒泡排序的思想,每次用异或交换相邻两项,可以过第一档部分分。
考虑优化这个过程。考虑每次将一个最大的数换到序列最后的方法。设
若此时 (d) 是最大的数,我们想把它换到最后面,设 $oplus $ 表示二进制异或操作,我们可以先将序列变为
对于 (doplus e) 后面的元素,我们将它们按顺序与前一项异或,容易发现序列变为
那么我们就将 (d) 换到队尾去了。为了消除 (d) 的影响,我们将前面的元素也进行类似的操作
此时对于缩小了一个长度的序列重复进行求解即可。注意每次寻找的是原数列上的最大值而非每次更新完后的值。
操作次数为 (frac 12 imes 2n^2=n^2)。
- 严格单调不降序列
从高位向低位考虑,每次我们仅考虑序列这一位的数,若非全 (0) 则一定能通过异或将其变为只剩一个 (1) 。从左往右考虑清零,若 (a_i) 该位为 (1),那么我们让 (a_{i+1}oplus a_i) 使该位为 (1)(若本为 (1) 则不操作)。操作完后使最后一位为 (1),并将序列长度减 (1)(结尾位置前移)。
注意当有一位为全 (0) 的时候我们不能将序列长度缩短,因为我们保证序列单调不降的方法是后面的最高位二进制位数比前面的大,全 (0) 的话会导致不满足并被卡。虽然实际上该题数据没有卡这个。
实现
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cmath>
#include<utility>
using namespace std;
inline int read(){
int w=0,x=0;char c=getchar();
while(!isdigit(c))w|=c=='-',c=getchar();
while(isdigit(c))x=x*10+(c^48),c=getchar();
return w?-x:x;
}
namespace star
{
const int maxn=1005;
int n,a[maxn];
int Ans;
pair<int,int> ans[1000005];
inline void work1(){
static int b[maxn];
for(int i=1;i<=n;i++) a[i]=b[i]=read();
for(int len=n-1;~len;len--){
int x=0;
for(int i=1;i<=len+1;i++) if(b[i]>b[x]) x=i;
for(int i=1;i<=len+1 and i<n;i++) a[i]^=a[i+1],ans[++Ans]=make_pair(i,i+1);
for(int i=x;i<=len;i++) a[i+1]^=a[i],ans[++Ans]=make_pair(i+1,i),swap(b[i],b[i+1]);
for(int i=x-1;i>1;i--) a[i-1]^=a[i],ans[++Ans]=make_pair(i-1,i);
}
}
inline void work2(){
for(int i=1;i<=n;i++) a[i]=read();
for(int cnt=0,k=19;~k;k--){
bool ok=false;
for(int i=1;i<=n-cnt;i++) ok|=(a[i]>>k)&1;
if(!ok) continue;
for(int i=1;i<n-cnt;i++) if((a[i]>>k)&1){
if((a[i+1]>>k)&1^1) a[i+1]^=a[i],ans[++Ans]=make_pair(i+1,i);
a[i]^=a[i+1],ans[++Ans]=make_pair(i,i+1);
}
++cnt;
}
}
}
signed main(){
star::n=read();
if(read()==1) star::work1();
else star::work2();
printf("%d
",star::Ans);
for(int i=1;i<=star::Ans;i++) printf("%d %d
",star::ans[i].first,star::ans[i].second);
return 0;
}