AtCoder Beginner Contest 381-E

AtCoder Beginner Contest 381-E

Problem

一个长度为奇数、最中间的那个字符是 /、左边所有字符都是都是 1、右边所有字符都是 2 的字符串被称为11/22 字符串

更加严谨的定义:

当一个字符串 T 满足以下所有条件时,它被称为11/22 字符串

  • |T| 是奇数。这里, |T| 表示 T 的长度。
  • 从第 1 到第(|T|+121) 个字符都是 1
  • (|T|+12) 个字符是/
  • 从第 (|T|+12+1) 到第 |T| 的字符都是 2

例如,11/22111/222/是 11/22 字符串,但11221/22211/22222/11/2/2/211则不是。

给定由12/组成的字符串 S|S|=N。有 Q 次询问:给定 LR,设 TS 的从第 L 个字符到第 R 个字符组成的子串。请找出 T 的最长子序列使得该子序列是一个 11/22 字符串。如果不存在这样的子序列,则打印 "0"。

Constraints

  • 1N105
  • 1Q105
  • S is a string of length N consisting of 1, 2, and /.
  • 1LRN
  • N, Q, L, and R are integers.

Solution

考虑预处理出所有/的位置,记作 posi

使用前缀和后缀和计算“每一个字符之前有多少个1”和“每一个字符之后有多少个2”,分别记作 left1iright2i

对于每次询问,一个简单暴力的算法就是,枚举在该区间内的所有/,并看看其左右分别有多少个12,则选择该/作为11/22子串的中间的那个/能够找出的最长子序列长度为

(1)ansi=min{left1posileft1L,right2posiright2R}LposiR

即可以利用预处理的信息,在O(1)的时间复杂度内计算出选择某个/时的答案。

并且可以通过二分 posi 来得到需要枚举的 i 的范围。

但是这样还是需要枚举 [L,R] 内的所有/,如果/很多就寄了。

观察上式可以发现,随着 posi 增大,left1posileft1L 单调递增,right2posiright2R 单调递减。min{,} 一定是一个开口向下的单峰函数,所以可以使用三分来确定最佳的 posi

Code

#define N 1000010


int left1[N],left2[N],right1[N],right2[N];
int n,q;
string str;
int pos[N],cnt;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cout.precision(10);
	int t=1;
//	cin>>t;
	while(t--)
	{
		cin>>n>>q;
		cin>>str;
		str=" "+str;
//		pos[++cnt]=0;
		for(int i=1;i<=n;i++)
		{
			if(str[i]=='/')
			{
				pos[++cnt]=i;
			}
		}
		pos[++cnt]=n+1;
		int pre1=0,pre2=0;
		for(int i=1;i<=n;i++)
		{
			left1[i]=pre1;
			left2[i]=pre2;
			if(str[i]=='1') pre1++;
			if(str[i]=='2') pre2++;
		}	
		pre1=pre2=0;
		for(int i=n;i>=1;i--)
		{
			right1[i]=pre1;
			right2[i]=pre2;
			if(str[i]=='1') pre1++;
			if(str[i]=='2') pre2++;
		}
		
		while(q--)
		{
			int a,b,ans=0;cin>>a>>b;
			// 确定左右边界,即找到最远的一对杠但是在[a,b]之内
			int l=lower_bound(pos+1,pos+cnt+1,a)-pos;
			int r=upper_bound(pos+1,pos+cnt+1,b)-pos-1;
//			DEBUG1(l);
//			DEBUG1(r);
			while(l+3000<r)
			{
				int mid1=l+(r-l)/3;
				int mid2=r-(r-l)/3;
				int p1=pos[mid1],p2=pos[mid2];
				int s1=min(left1[p1]-left1[a],right2[p1]-right2[b]);
				int s2=min(left1[p2]-left1[a],right2[p2]-right2[b]);
				if(s1<s2)
				{
					l=mid1;
				}
				else{
					r=mid2;
				}
			}
			for(int i=l;i<=r;i++)
			{
				int p=pos[i];
				if(str[p]=='/')
				{
//					cout<<"calc "<<i<<endl;
//					cout<<min(left1[p]-left1[a],right2[p]-right2[b])<<endl;
					ans=max(ans,1+2*min(left1[p]-left1[a],right2[p]-right2[b]));
				}
			}
			cout<<ans<<endl;
		}
		
	}
	return 0;
}
posted @   Vanilla_chan  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示