题解 [ABC381E] 11/22 Subsequence

【洛谷专栏】

赛时竟然把好多暴力做法放过了,不能理解出题人的数据强度。

但是这里讲的是正确解法,不是暴力。

题意

当一个字符串 \(T\) 满足以下所有条件时,它就是好的字符串:

  • \(\vert T \vert \bmod 2 = 1\)
  • \(T[1 : \frac{\vert T \vert +1}{2} - 1] = \texttt{1}\)
  • \(T[\frac{|T|+1}{2}] = \texttt{/}\)
  • \(T[\frac{|T|+1}{2} + 1 : \vert T \vert ] = \texttt{2}\)

给定长度为 \(N\)12/组成字符串 \(S\),有 \(Q\) 次询问 \(L\)\(R\)。假设 \(T = S[L:R]\)。求 \(T\) 的好的子序列(不一定连续)的最大长度。如果不存在这样的则输出 \(0\)

分析

以任意一个 / 作为中心点,分别不断向左边和右边扩展 12,最长的子串取决于 12 数量的最小值。

这个问题貌似并不是很好用数据结构维护,想到二分答案。

设当前二分的长度为 \(x\),判定即找到 \(L\) 的右边(包含 \(L\))的第 \(x\)1\(R\) 的左边(包含 \(R\))的第 \(x\)2,判断这两个位置中间是否有 /

判断一个区间是否有 / 可以使用前缀和,于是现在的问题变成了如何能快速的找到第 \(x\) 个,直接暴力跳肯定是不行的,可以使用倍增预处理。

接着就可以发现这个二分是不必要的,直接倍增求答案即可。

时间复杂度 \(O(n \log n + Q \log n)\),空间复杂度 \(O(n \log n)\)

代码

//the code is from chenjh
#include<cstdio>
#define MAXN 100005
using namespace std;
int n,q;
char s[MAXN];
int a[MAXN],nx[MAXN][18],pr[MAXN][18];//'/' 字符的前缀和,下一个 2,上一个 1。
int main(){
	scanf("%d%d %s",&n,&q,s+1);
	for(int i=1;i<=n;i++) a[i]=a[i-1]+(s[i]=='/');
	for(int i=1;i<=n+1;i++) pr[i][0]=i&&s[i-1]=='2'?i-1:pr[i-1][0];
	nx[n+1][0]=n+1;//注意边界情况。
	for(int i=n;i>=0;--i) nx[i][0]=i<n&&s[i+1]=='1'?i+1:nx[i+1][0];
	for(int k=1;k<18;k++)
		for(int i=0;i<=n+1;i++) pr[i][k]=pr[pr[i][k-1]][k-1],nx[i][k]=nx[nx[i][k-1]][k-1];//倍增预处理。
	for(int l,r;q--;){
		scanf("%d%d",&l,&r);
		bool fl=a[r]-a[l-1];//判断这段区间是否有 /,如果没有答案一定为 0。
		int ans=0;
		--l,++r;//因为包含,所以向两边多跳一个。
		for(int k=17,x,y;k>=0;--k){
			x=nx[l][k],y=pr[r][k];
			if(x<y&&a[y]-a[x-1]>0) ans|=1<<k,l=x,r=y;
		}
		printf("%d\n",ans?ans<<1|1:(int)fl);
	}
	return 0;
}
posted @ 2025-02-09 20:52  Chen_Jinhui  阅读(20)  评论(0)    收藏  举报