AT_abc331_f [ABC331F] Palindrome Query 題解

harmis_yz發表於2024-03-05

分析

線段樹。

每個節點維護兩個值:$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;
}

相關文章