首页 > 其他分享 >AT_abc331_f [ABC331F] Palindrome Query 题解

AT_abc331_f [ABC331F] Palindrome Query 题解

时间:2024-03-05 19:14:03浏览次数:28  
标签:Palindrome rs int 题解 tr ls abc331 now il

分析

线段树。

每个节点维护两个值:$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;
}

标签:Palindrome,rs,int,题解,tr,ls,abc331,now,il
From: https://www.cnblogs.com/harmisyz/p/18054670

相关文章

  • AT_abc287_g [ABC287G] Balance Update Query 题解
    分析一眼分块。用值域分块来维护。先把所有的值离散化,使得至于不大于$n+q$。统计一下每个值的数量,每个块包含值的数量,每个块的价值和。修改值的时候先把原来值的数量,块包含的数量,块的价值剪掉被修改值的贡献,然后在新的值上面更新。修改数量直接改数量的变化贡献即可。找前$x$......
  • P8844 [传智杯 #4 初赛] 小卡与落叶 题解
    分析乱搞题。$1\len,m\le10^5$的时候就可以考虑乱搞了。发现每次操作$1$都会把上一次的操作$1$覆盖掉,那么第$i$个询问时树的颜色情况就是由前$1$个操作$1$决定。也就是说这个询问的内容变成了:在$x$为根的子树中,深度不小于$x'$的节点数量。$x'$是该操作$1$......
  • AT_abc287_g [ABC287G] Balance Update Query 题解2
    分析权值线段树。给每个节点赋一个值$val$和$a_i=val$的$b_i$之和。修改$a_x$的时候先将$a_x$的出现次数在树上剪掉$b_x$,再在$y$上面加上;修改$b_x$的时候直接加上变化量$y-b_x$。由于我们是要取前$x$大的$a_i$之和,在询问的时候有限考虑右儿子,然后在是当前......
  • AT_abc335_f [ABC335F] Hop Sugoroku 题解
    比E简单。分析考虑暴力DP。定义状态函数$f_i$表示最后一个黑点为$i$时的方案数,有:$f_i=\sum\limits_{j=1}^{i-1}f_j[(i-j)\bmodval_j=0]$。不难发现在使用刷表法的时候,转移代码:for(reintj=1;i+val[i]*j<=n;++j)f[i+val[i]*j]=(f[i+val[i]*j]+f[i])%p;这玩意复杂......
  • AT_abc190_e [ABC190E] Magical Ornament 题解
    分析考虑状压。定义状态函数$f_{i,j}$表示在得到$C$出现过的状态为$i$且排列末尾为$j$时的最小代价。则有转移方程:$f_{i,j}=\min{f_{i',k}+dis_{k,j}}$,保证$i'$表示集合属于$i$。$dis_{i,j}$跑最短路就行了,通过枚举$C_i$为起点可以做到$O(kn\logn)$的复杂度求......
  • CF1800F Dasha and Nightmares 题解
    分析考虑枚举。注意到第二个条件是必须要有$25$个字符在里面出现过,故考虑枚举唯一没出现过的字符$k$,然后再枚举$s_i$。令$cnt_{i,j}$表示$s_i$中字符$c$出现的奇偶性。如果有字符$c\nek\landcnt_{i,c}=0$,则在$s_j$中必有$cnt_{j,c}=1$;反之同理。枚举字符$c......
  • P5655 基础数论函数练习题 题解
    分析考虑莫队。令$S=\operatorname{lcm}(a_l,a_{l+1},a_{l+2},\dots,a_{r-1})$。则对于新加进来的$a_r$,有:$$\operatorname{lcm}(a_l,a_{l+1},a_{l+2},\dots,a_{r-1},a_r)\=\operatorname{lcm}(S,a_r)\=\frac{S\timesa_r}{\gcd(S,a_r)}$$很容易发现,$S$在不取模的情况下会......
  • CF163A Substring and Subsequence 题解
    分析考虑DP。定义状态函数$f_{i,j}$表示在$s[1\dotsi]$中选一个子串$a$,在$t[1\dotsj]$中选一个子序列$b$,且$s_i,t_j$必选时$a=b$的方案数。则有两种情况:$s_i\net_j$。此时$a$和$b$是不可能相同的了,所以$f_{i,j}=0$。$s_i=t_j$。在只选$s_i,t_j$时......
  • CF101B Buses 题解
    分析考虑DP。由于$n$很大,而$m$可以接受,考虑根据公交车定义状态函数。很容易想到一种状态函数:$f_i$表示做第$i$辆公交车到$t_i$的方案数。根据题意,就有转移方程:$f_i=\sumf_j[s_i\let_j\let_i-1]+k$,$k$在$s_i=0$时为$1$,其余为$0$。然后这题就会了。求和部分......
  • AT_abc263_f [ABC263F] Tournament 题解
    分析一眼DP。定义状态函数$f_{i,j}$表示在第$i$此比赛中,获胜者为$j$时的最大奖学金。把比赛过程看成一棵倒着的满二叉树,就能发现:第$i$场比赛只会是其左儿子为根的子树中叶子节点的某一个与其右儿子为根的子树中叶子节点的某一个进行比赛。然后就可以得到转移方程:$f_{i,......