Loading

ARC060F 题解

题意:给出字符串 \(S\),把 \(S\) 分成若干段,每一段都不存在正周期,求最小段数以及有多少种使得段数最小的分法,第二个数模 \(10^9+7\)

顶级诈骗题

  • \(S\) 不存在正周期,那么答案为 \(1,1\)

  • \(S\) 所有字符相同,那么答案为 \(n,1\)

  • 否则,第一问答案为 \(1\)

考虑证明。前两个结论显然,证明第三个结论。

  • 弱周期引理:若 \(p,q\)\(S\) 的周期(不是正周期),且 \(p+q\le |S|\),那么 \(\gcd(p,q)\) 也是 \(S\) 的周期。

证明:钦定 \(p>q\),根据已知,当 \(i+p\le |S|\) 时,\(s_i=s_{i+p}\);当 \(i-q>0\) 时,\(s_i=s_{i-q}\)。不难发现,由于 \(p+q\le|S|\),对于任意 \(1\le i\le n\),都有一者成立。对于前者,\(s_i=s_{i+p}=s_{i+p-q}\);对于后者,\(s_i=s_{i-q}=s_{i-q+p}=s_{i+p-q}\)。总之能推出 \(s_i=s_{i+p-q}\)。那么 \(p-q\) 也是 \(|S|\) 的周期,这个过程很像更相减损术,然后归纳证明 \(s_i=s_{i+\gcd(p,q)}\)

对于原结论,证明:

根据已知,\(S\) 存在正周期,设周期长度为 \(p\)。那么我们把 \(S\) 分成 \(S_{1...n-1},S_{n...n}\) 两部分,考虑反证,设 \(S_{1...n-1}\) 存在正周期长度 \(q(q<n-1)\)

显然 \(p\) 仍然是 \(S_{1...n-1}\) 的一个周期,又由 \(p\)\(S\) 的正周期以及 \(q\)\(S_{1...n-1}\) 的正周期得 \(p\le \frac n2,\space q\le \frac {n-1}2\),显然 \(p+q\le n\),根据弱周期引理,\(\gcd(p,q)\) 也是 \(S_{1...n-1}\) 的一个周期。

\(x=\gcd(p,q)\)。已知 \(S_{1...n-1}\) 并不是所有字符相同,所以不存在长度为 \(1\) 的周期,即 \(x>1\),若 \(p=ax,\space q=bx\),而 \(n,n-1\) 分别是 \(p,q\) 的倍数,也都是 \(x\) 的倍数,即 \(x|\gcd(n,n-1)\),而 \(x>1\),相当于 \(\gcd(n,n-1)>1\),矛盾。

\(S_{1...n-1}\) 不存在正周期。

对于第二问,我们可以直接枚举两段的分割点,正反跑 \(\text{KMP}\) 预处理每个前/后缀的 \(\text{Border}\)

时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=5e5+10;
char s[maxn];
ll n,ans,f1[maxn],f2[maxn];
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	ll fl=1;
	for(ll i=2;i<=n;i++)
		if(s[i]!=s[i-1])
		{
			fl=0;
			break;
		}
	if(fl)
	{
		printf("%lld\n1",n);
		return 0;
	}
	for(ll i=2,j=0;i<=n;i++)
	{
		while(j&&s[j+1]!=s[i]) j=f1[j];
		if(s[j+1]==s[i]) f1[i]=++j;
	}
	f2[n]=f2[n+1]=n+1;
	for(ll i=n-1,j=n+1;i;i--)
	{
		while(j<n+1&&s[j-1]!=s[i]) j=f2[j];
		if(s[j-1]==s[i]) f2[i]=--j;
		else f2[i]=n+1;
	}
	if(f1[n]==0||n%(n-f1[n])) printf("1\n1");
	else
	{
		for(ll i=1;i<n;i++)
			if((f1[i]==0||i%(i-f1[i]))&&(f2[i+1]==n+1||(n-i)%((n-i)-(n-f2[i+1]+1)))) ++ans;
		printf("2\n%lld",ans);
	}
	return 0;
}
posted @ 2023-08-08 15:47  Lgx_Q  阅读(16)  评论(0编辑  收藏  举报