AT_abc331_f [ABC331F] Palindrome Query 题解

分析

线段树。

每个节点维护两个值:\(s[l\dots r]\)\(s[r \dots l]\)。判断字串是否是回文直接就是询问的答案维护出来的两个值是否相同。

首先想到用线段树暴力维护。第一个值很显然是两个儿子的第一个值加起来,第二个值是反着加起来。得到很酷的代码:

il void up(int now){
	tr[now].ls=tr[now<<1].ls+tr[now<<1|1].ls,
	tr[now].rs=tr[now<<1|1].rs+tr[now<<1].rs;
	return ;
}
il void build(int now,int l,int r){
	tr[now].l=l,tr[now].r=r;
	if(l==r) tr[now].ls=tr[now].rs=s[l-1];
	else build(now<<1,l,(l+r)>>1),build(now<<1|1,((l+r)>>1)+1,r),up(now);
	return ; 
}
il void insert(int now,int k,int c){
	if(tr[now].l==tr[now].r){tr[now].ls=tr[now].rs=c;}
	else{
		int mid=tr[now].l+tr[now].r>>1;
		if(k<=mid) insert(now<<1,k,c);
		else insert(now<<1|1,k,c);
		up(now);
	}
	return ;
}
il tree query(int now,int l,int r){
	tree ans={0,0,"",""};
	if(tr[now].l>=l&&tr[now].r<=r) return tr[now];
	int mid=tr[now].l+tr[now].r>>1;
	if(l<=mid) ans=query(now<<1,l,r);
	if(mid<r){
		tree ans1=query(now<<1|1,l,r);
		ans.ls=ans.ls+ans1.ls,ans.rs=ans1.rs+ans.rs;
	}
	return ans;
}

然后就 TLE 了。原因是两个字符串相加复杂度太高。

考虑把字符串转化成数字。在字符串哈希中,第 \(i\) 个字符的值可以被表示为 \(s_i \times b^{len-i} \bmod k\)。这里 \(b\) 的值设一个大于 \(128\) 的就行了(我记得是)。

然后就没了。改变上传的价值。定义那时候的 \(len=r-l+1\),就可以把两个值算出来,即:\(fa.x=lson.x \times b^{r-len}+rson.x,fa.y=rson.y\times b^{mid-l+1} +lson.y\)

询问的时候在节点右端点包含询问区间右端点时注意一下左右限制即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register
#define il inline

const int N=1e6+10,p=1e9+7;
int n,q;
string s;
int b[N],bk=190;
struct tree{
	int l,r;
	int ls,rs;
}tr[N<<2];
il void up(int now){
	int mid=tr[now].l+tr[now].r>>1;
	tr[now].ls=(tr[now<<1].ls*b[tr[now].r-mid]%p+tr[now<<1|1].ls)%p,
	tr[now].rs=(tr[now<<1|1].rs*b[mid-tr[now].l+1]%p+tr[now<<1].rs)%p;
	return ;
}
il void build(int now,int l,int r){
	tr[now].l=l,tr[now].r=r;
	if(l==r) tr[now].ls=tr[now].rs=(s[l-1]+1-'a');
	else build(now<<1,l,(l+r)>>1),build(now<<1|1,((l+r)>>1)+1,r),up(now);
	return ; 
}
il void insert(int now,int k,int c){
	if(tr[now].l==tr[now].r){tr[now].ls=tr[now].rs=c;}
	else{
		int mid=tr[now].l+tr[now].r>>1;
		if(k<=mid) insert(now<<1,k,c);
		else insert(now<<1|1,k,c);
		up(now);
	}
	return ;
}
il tree query(int now,int l,int r){
	tree ans={0,0,0,0};
	if(tr[now].l>=l&&tr[now].r<=r) return tr[now];
	int mid=tr[now].l+tr[now].r>>1;
	if(l<=mid) ans=query(now<<1,l,r);
	if(mid<r){
		tree ans1=query(now<<1|1,l,r);
		if(l<=mid)
			ans.ls=(ans.ls*b[min(r,tr[now].r)-mid]%p+ans1.ls)%p,
			ans.rs=(ans1.rs*b[mid-max(tr[now].l,l)+1]%p+ans.rs)%p;
		else ans=ans1;
	}
	return ans;
}

il void solve(){
	cin>>n>>q>>s;
	b[0]=1;for(re int i=1;i<=n;++i) b[i]=b[i-1]*bk%p;
	build(1,1,n);
	for(re int i=1;i<=q;++i){
		int op;cin>>op;
		if(op==1){
			int k;char c;cin>>k>>c;
			insert(1,k,c+1-'a');
		}
		else{
			int l,r;cin>>l>>r;
			tree ans=query(1,l,r);
			if(ans.ls==ans.rs) cout<<"Yes\n";
			else cout<<"No\n";
		} 
	}
	return ;
}

signed main(){
	solve();
	return 0;
}
posted @ 2024-03-05 18:57  harmis_yz  阅读(62)  评论(0)    收藏  举报