[CSP-S模拟测试]:stone(结论+桶+前缀和+差分)

题目描述

$Cab$有两行石子,每个石子上有一个字母,为$'C''A''B'$中的一个。
一开始,在每行第一个石子上站着一只$lucky$,$Cab$每次可以选择一个字母,使得所站石子上字母为该字母的$lucky$向前走一步,如果此时$lucky$已经到了一行石子的结尾就会掉出去,$Cab$显然不会这么做。
一个数对$(x,y)$是$lucky$的,当且仅当在$lucky$不掉出去的前提下,通过一些操作能使第一行的$lucky$处于第$x$个石子的同时第二只$lucky$处于第$y$个石子。
请求出有多少个$lucky$的数对。


输入格式

第一行一个长度为$n$的字符串表示第一行石子。
第二行一个长度为$m$的字符串表示第二行石子。


输出格式

输出一个答案表示$lucky$的数对个数。


样例

样例输入:

CAB
ABCAB

样例输出:

11


数据范围与提示

对于$30\%$的数据:$n\leqslant 1,000,m\leqslant 1,000$。
对于另$30\%$的数据:$n\leqslant 50,000,m\leqslant 50,000$,且两个字符串中只含有两种字母。
对于$100\%$的数据:$n\leqslant 1,000,000,m\leqslant 1,000,000$。


题解

官方题解画了一堆图,我也没看懂。

对于第一个串中的每一个点,其有一个覆盖范围$(l,r)$,可以用贪心的思想,$l$即为尽可能让其不动;$r$则为尽可能让它动;注意边界即可。

但是打个表会发现,这中间有一些点还是不能取到;再认真看一下,会发现对于当前点$a_i$,如果$a_i=b_j$且$a_{i-1}=b_{j+1}$,那么这个$(i,j)$是不可取的。

那么答案就是:

$$ans=\sum \limits_{i=1}^n r_i-l_i+1-num[i]$$

上式中的$num[i]$即为$l_i\sim r_i$中$a_i=b_j$且$a_{i-1}=b_{j+1}$的个数。

$num$数组可以用桶$+$前缀和$+$差分处理。

如果你发现$WA90$的话可以考虑将两个串的读入顺序交换即可。

时间复杂度:$\Theta(n+m)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int a[1000001],b[1000001],la,lb;
char ch1[1000001],ch2[1000001];
int l[1000001],r[1000001];
int t[1000001][6];
long long ans;
int main()
{
	scanf("%s%s",ch1+1,ch2+1);
	la=strlen(ch2+1);
	lb=strlen(ch1+1);
	for(int i=1;i<=la;i++)
		a[i]=ch2[i]-'A'+1;
	for(int i=1;i<=lb;i++)
		b[i]=ch1[i]-'A'+1;
	int faill=1,failr=1;
	for(int i=1;i<=la;i++)
	{
		while(a[i]!=b[failr]&&failr<lb)failr++;
		l[i]=faill;r[i]=failr;
		if(a[i]==b[faill]&&faill<lb)faill++;
		if(failr<lb)failr++;
	}
	for(int i=2;i<=lb;i++)
	{
		if(b[i-1]==1&&b[i]==2)t[i][0]++;
		if(b[i-1]==2&&b[i]==1)t[i][1]++;
		if(b[i-1]==1&&b[i]==3)t[i][2]++;
		if(b[i-1]==3&&b[i]==1)t[i][3]++;
		if(b[i-1]==2&&b[i]==3)t[i][4]++;
		if(b[i-1]==3&&b[i]==2)t[i][5]++;
		t[i][0]+=t[i-1][0];
		t[i][1]+=t[i-1][1];
		t[i][2]+=t[i-1][2];
		t[i][3]+=t[i-1][3];
		t[i][4]+=t[i-1][4];
		t[i][5]+=t[i-1][5];
	}
	ans=r[1]-l[1]+1;
	for(int i=2;i<=la;i++)
	{
		ans+=r[i]-l[i]+1;
		if(a[i]==a[i-1])continue;
		if(a[i]==1&&a[i-1]==2)ans-=t[r[i]][0]-t[l[i]-1][0];
		if(a[i]==2&&a[i-1]==1)ans-=t[r[i]][1]-t[l[i]-1][1];
		if(a[i]==1&&a[i-1]==3)ans-=t[r[i]][2]-t[l[i]-1][2];
		if(a[i]==3&&a[i-1]==1)ans-=t[r[i]][3]-t[l[i]-1][3];
		if(a[i]==2&&a[i-1]==3)ans-=t[r[i]][4]-t[l[i]-1][4];
		if(a[i]==3&&a[i-1]==2)ans-=t[r[i]][5]-t[l[i]-1][5];
	}
	printf("%lld",ans);
	return 0;	
}

rp++

上一篇:快照读的弊端


下一篇:【luogu P6091】【模板】原根