AtCoder Beginner Contest 150 F. Xor Shift (异或性质+扩展kmp)

题目

给两个长为n(n<=2e5)的序列a和b,

你可以自行选择一个k(0<=k<n)和一个x(x>=0),

构造新序列c[i]=a[(i+k)\%n]\bigoplus x,其中\bigoplus为异或

使得c序列和b序列完全相等,

输出所有合法的(k,x)

思路来源

https://www.luogu.com.cn/problem/solution/AT_abc150_f

题解

考虑n=3时的两个序列,

a[0] a[1] a[2]

b[0] b[1] b[2]

不妨假设k=1时,能找到对应的x使式子成立,有:

a[1]\bigoplus x=b[0]\ (1)\\ a[2]\bigoplus x=b[1]\ (2)\\ a[3]\bigoplus x=b[2]\ (3)

相邻项两两异或,有

a[1] \bigoplus a[2]=b[0]\bigoplus b[1]\\ a[2] \bigoplus a[0]=b[1]\bigoplus b[2]\\ a[0] \bigoplus a[1]=b[2]\bigoplus b[0]\\

也就是,只要存在某个循环偏移量k,

使得a的相邻项异或值,和b的相邻项异或值,逐项相等

总能找到x,使得(k,x)合法,

代入(1)式中,显然有x=a[k]\bigoplus b[0]

所以,对相邻项异或,构造两个新序列s1和s2,

问题转化成,对于s1的循环串,有多少个是和s2完全相同的

将s1扩展成原来的2倍之后(s1=s1+s1),与s2做扩展kmp即可

代码

// Problem: F - Xor Shift
// Contest: AtCoder - AtCoder Beginner Contest 150
// URL: https://atcoder.jp/contests/abc150/tasks/abc150_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
const int N=2e5+10,M=4e5+10;
int n,a[N],b[N],net[N],ex[N],s1[M],s2[N];
vector<P>ans;
void extkmppre(int s[],int len){
	int i=0,j,pos;
	net[0]=len;
	while(i+1<len&&s[i]==s[i+1])i++;
	net[1]=i,pos=1;
	rep(i,2,len-1){
		if(net[i-pos]+i<net[pos]+pos){
			net[i]=net[i-pos];
		}
		else{
			j=net[pos]+pos-i;
			if(j<0)j=0;
			while(i+j<len&&s[j]==s[i+j])j++;
			net[i]=j,pos=i;
		}
	}
}
void extkmp(int s1[],int s2[],int l1,int l2){
	int i=0,j,pos;
	extkmppre(s2,l2);
	while(i<l2&&i<l1&&s1[i]==s2[i])i++;
	ex[0]=i,pos=0;
	if(ex[0]==l2)ans.pb(P(0,a[0]^b[0]));
	rep(i,1,l1-1){
		if(net[i-pos]+i<ex[pos]+pos){
			ex[i]=net[i-pos];
		}
		else{
			j=ex[pos]+pos-i;
			if(j<0)j=0;
			while(i+j<l1&&j<l2&&s1[i+j]==s2[j])j++;
			ex[i]=j,pos=i;	
		}
		if(ex[i]==l2)ans.pb(P(i,a[i]^b[0]));
	}
}
int main(){
	sci(n);
	rep(i,0,n-1)sci(a[i]);
	rep(i,0,n-1)sci(b[i]);
	rep(i,0,n-1){
		s1[i]=a[i]^a[(i+1)%n];
		s1[i+n]=s1[i];
	}
	rep(i,0,n-1)s2[i]=b[i]^b[(i+1)%n];
	extkmp(s1,s2,2*n,n);
	if(!ans.empty() && ans.back().fi>=n)ans.pop_back();//aa a,匹配位置重复算了两次
	for(auto &x:ans){
		printf("%d %d\n",x.fi,x.se);
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值