CF452F Permutation(线段树维护字符串哈希hash值)

题目链接:Permutation - 洛谷

考虑在遍历序列中每个数的同时,动态维护一个数组a,a[x]=0表示当前x没出现过,a[x]=1表示当前x已经出现过了,每次修改后还要检查以x位置为中心,延伸到最长位置,这段a的子序列是否为回文串。比如,n=6,考虑当前加入4,令a[4]=1,并且检查a[2~6]是否为回文,如果是回文,说明:

  1. 3和5都要么已经出现,要么都没出现(也就是要么都在4左边,要么都在4右边)
  2. 2和6也有同样的结论

所以“暂时没有出现等差数列”。

每次操作都这样维护即可,需要用线段树维护双向hash值,具体见代码。 

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=(a), (i##i)=(b); i<=(i##i); ++i)
#define ROF(i,a,b) for(int i=(a), (i##i)=(b); i>=(i##i); --i)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
#define ull unsigned long long
#define ls o<<1
#define rs o<<1|1
const int N = 5e5+5, mod=1e9+7, bs=131;
int n;
ull pw[N];
struct node{
	ull val; int len;
	node(ull v=0, int l=0){val=v, len=l;}
}t1[N<<2], t2[N<<2];

node merge(node l,node r){
	node res=node(0,0);
	res.val = (l.val*pw[r.len]%mod + r.val)%mod;
	res.len = l.len+r.len;
	return res;
}
void pushup(int o){
	t1[o]=merge(t1[ls], t1[rs]); //左右合并
	t2[o]=merge(t2[rs], t2[ls]); //右左合并
}
void build(int o,int l,int r){
	if(l==r){
		t1[o]=t2[o]=node(0,1); //hash值为0,len为1
		return;
	}
	int mid=l+r>>1;
	build(ls,l,mid), build(rs,mid+1,r);
	pushup(o);
}
void upd(int o,int l,int r,int pos){
	if(l==r){
		t1[o]=t2[o]=node(1,1); //修改,把该位置1
		return;
	}
	int mid=l+r>>1;
	if(pos<=mid) upd(ls,l,mid,pos);
	else upd(rs,mid+1,r,pos);
	pushup(o);
}
node query1(int o,int l,int r,int x,int y){ //t1的询问
	if(x>y) return node(0,0);
	if(x<=l && r<=y) return t1[o];
	int mid=l+r>>1;
	if(y<=mid) return query1(ls,l,mid,x,y);
	else if(x>mid) return query1(rs,mid+1,r,x,y);
	node L=query1(ls,l,mid,x,y), R=query1(rs,mid+1,r,x,y);
	return merge(L,R);
}
node query2(int o,int l,int r,int x,int y){ //t2的询问
	if(x>y) return node(0,0);
	if(x<=l && r<=y) return t2[o];
	int mid=l+r>>1;
	if(y<=mid) return query2(ls,l,mid,x,y);
	else if(x>mid) return query2(rs,mid+1,r,x,y);
	node L=query2(ls,l,mid,x,y), R=query2(rs,mid+1,r,x,y);
	return merge(R,L);
}
signed main(){
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	pw[0]=1; FOR(i,1,5e5) pw[i]=pw[i-1]*bs%mod; //预处理pow数
	int T=1; cin>>T;
	while(T--){
		cin>>n;
		build(1,1,n);
		FOR(i,1,n){
			int x; cin>>x; //输入序列的每个值
			upd(1,1,n,x); //x位置从0改为1
			int d=min(x-1,n-x); //延伸半径
			if(d<=0) continue;
			if(query1(1,1,n,x-d,x-1).val != query2(1,1,n,x+1,x+d).val){cout<<"Y\n"; return 0;}
		}
		cout<<"N\n";
	}
	// return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值