CF452F. Permutation

发布时间 2023-07-24 11:47:37作者: xx019

很有趣的一道题。双倍经验:P2757 [国家集训队] 等差子序列

要找三个数构成等差序列,一个直接的想法就是枚举中间的数 \(a_i\),然后看它左右两边是不是有 \(a_i-k\)\(a_i+k\)。这个枚举的过程已经不好再优化了,我们考虑从判断两边的值入手。

注意到题目中说 \(a\) 是一个排列,我们很自然的想到在值域上解决问题。可以从左往右扫,每次把值域上 \(a_i\) 这个位置置为 1,这样判断条件等价于判断是否有位置 \(a_i-k,a_i+k\) 值不同,直接线段树维护 hash 判断回文串即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,SIZ=5e5;
const int B=1145141,I=738796363,P=1e9+7;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,a[500005],pw[500005],ip[500005];
struct segtree{
	#define ls p<<1
	#define rs p<<1|1
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int s;
	}c[2000005];
	void pushup(int p){
		c[p].s=(c[ls].s+c[rs].s)%P;
	}
	void build(int l,int r,int p){
		if(l==r){c[p].s=0;return;}
		int mid=(l+r)>>1;
		build(lson),build(rson);
		pushup(p);
	}
	void update(int l,int r,int p,int x,int k){
		if(l==r){c[p].s=k;return;}
		int mid=(l+r)>>1;
		if(x<=mid)update(lson,x,k);
		else update(rson,x,k);
		pushup(p);
	}
	int query(int l,int r,int p,int L,int R){
		if(L>R)return 0;
		if(L<=l&&r<=R)return c[p].s;
		int mid=(l+r)>>1,res=0;
		if(L<=mid)res=(query(lson,L,R))%P;
		if(R>mid)res=(res+query(rson,L,R))%P;
		return res;
	}
	#undef ls
	#undef rs
	#undef lson
	#undef rson
}Tr1,Tr2;
signed main(){
	pw[0]=1;
	for(int i=1;i<=SIZ;i++)pw[i]=pw[i-1]*B%P;
	ip[0]=1;
	for(int i=1;i<=SIZ;i++)ip[i]=ip[i-1]*I%P;	
	int T=1;
	while(T--){
		n=read();for(int i=1;i<=n;i++)a[i]=read();
		Tr1.build(1,n,1);Tr2.build(1,n,1);int flag=0;
		for(int i=1;i<=n;i++){
			Tr1.update(1,n,1,a[i],pw[n-a[i]]);Tr2.update(1,n,1,a[i],pw[a[i]-1]);
			int len=min(a[i]-1,n-a[i]),l=a[i]-len,r=a[i]+len;
			int h1=Tr1.query(1,n,1,l,a[i])*ip[n-a[i]]%P;
			int h2=Tr2.query(1,n,1,a[i],r)*ip[a[i]-1]%P;
			if(h1!=h2){flag=1;break;}
		}
		puts((flag?"YES":"NO"));
	} 
	return 0;
}