P7362 [eJOI 2020 Day2] XOR Sort

P7362 [eJOI 2020 Day2] XOR Sort

题意

给你一个长度为 n 的序列,每次操作可以将一个数异或上相邻的一个数,求将序列改为严格单调递增序列或严格单调不降序列的操作次数的操作序列。

注意,改为严格单调递增序列的数据 n200 且每个数不相同,改为严格单调不降序列的数据 n1000

不要求操作次数最小,次数需不大于 40000,元素大小小于 220

思路

严格单调递增序列的数据实测如果改了数列很容易造成数字相同然后挂掉的情况出现。反而严格单调不降的条件用异或容易满足且数据范围大。我们考虑对数据分治。

  • 严格单调递增序列

我们考虑冒泡排序的思想,每次用异或交换相邻两项,可以过第一档部分分。

考虑优化这个过程。考虑每次将一个最大的数换到序列最后的方法。设

A={a,b,c,d,e,f,g}

若此时 d 是最大的数,我们想把它换到最后面,设 表示二进制异或操作,我们可以先将序列变为

{ab,bc,cd,de,ef,fg,g}

对于 de 后面的元素,我们将它们按顺序与前一项异或,容易发现序列变为

{ab,bc,cd,de,df,dg,d}

那么我们就将 d 换到队尾去了。为了消除 d 的影响,我们将前面的元素也进行类似的操作

{ad,bd,cd,de,df,dg,d}

此时对于缩小了一个长度的序列重复进行求解即可。注意每次寻找的是原数列上的最大值而非每次更新完后的值。

操作次数为 12×2n2=n2

  • 严格单调不降序列

从高位向低位考虑,每次我们仅考虑序列这一位的数,若非全 0 则一定能通过异或将其变为只剩一个 1 。从左往右考虑清零,若 ai 该位为 1,那么我们让 ai+1ai 使该位为 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\n",star::Ans);
	for(int i=1;i<=star::Ans;i++) printf("%d %d\n",star::ans[i].first,star::ans[i].second);
	return 0;
}
posted @   Star_Cried  阅读(157)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示