原题:


题意:
给你两个整数a和b,再给你一个正小数,整数部分忽略不计,只考虑小数部分的循环节,对于所有可能的循环节,令其长度为l,在小数部分循环出现的长度为p,最后一个循环节允许不完整,但是缺少的部分不计入循环长度
问你a*p-b*l的最小值是多少
考虑的循环节必须至少在小数部分中出现一次,小数部分的前缀可以不属于循环部分
这题也是一眼很棘手,和后缀数组和后缀自动机似乎都有相似之处
但是实际做法是kmp
翻转原串是关键思路,这样保证了能把原串前缀的不是循环部分的部分忽略掉,从而方便地利用next数组的性质
把小数部分串反过来,那么这个串的前缀就一定是循环部分
因为最后循环节允许不完整,所以考虑把一个循环节分成AB两部分,其中B是最后一个循环节缺的部分
考虑next数组的性质:next[i]表示子串[1,i]最长的前后缀长度,使得此长度的前后缀相等
那么对于ACA形式的串,其中A指最长的相同前后缀,把C接到A后边就可以变成ACA(C)的循环形式,前后缀有相交情况时同理
对于循环部分[1,i],出现长度就是i,出现长度固定,只需求出此时长度最小的循环节
由next的性质,保证[1,i-next[i]]是最小的循环节
可以反证,考虑ABABABABA,如果它被i-nxt[i]和next[i]+1划分为ABA BAB ABA,即认为[1,6]和[4,9]是相同的前后缀,且有多余的循环节被统计到了[1,i-nxt[i]]和[nxt[i]+1,i]里
那么此时可以从[1,i-nxt[i]]和[nxt[i],i]中各拿出一个循环节放到中间,易证此时仍然符合next[i]的性质,即之前求出的next[i]是错误的
所以得证,[1,i-next[i]]一定是最小的循环节
那么把小数部分倒过来,从左到右扫一遍,每次根据next[i]计算,然后统计到答案即可
代码:

1 #include<iostream>
2 #include<cstdio>
3 #include<algorithm>
4 #include<cstring>
5 #include<cmath>
6 using namespace std;
7 #define LL long long
8 LL a,b;
9 char s[11000000];
10 int c[11000000],n=0;
11 int nxt[11000000];
12 LL ans;
13 void gtnxt(){
14 int tmp=0;
15 nxt[1]=0;
16 for(int i=2;i<=n;++i){
17 while(tmp&&c[tmp+1]!=c[i]) tmp=nxt[tmp];
18 if(c[tmp+1]==c[i]) ++tmp;
19 nxt[i]=tmp;
20 /*if(nxt[i]){
21 if(nxt[i]>=i-nxt[i]+1) ans=max(ans,a*i-b*(i-nxt[i]));
22 if(nxt[i]==i-nxt[i]) ans=max(ans,a*i-b*nxt[i]);
23 ans=max(ans,a*i*/
24 if(nxt[i]) ans=max(ans,a*i-b*(i-nxt[i]));
25 }
26 }
27 int main(){
28 //freopen("ddd.in","r",stdin);
29 while(scanf("%lld%lld",&a,&b)!=EOF){
30 ans=-1e18,n=0;
31 scanf("%s",s);
32 for(int i=strlen(s)-1;i>=0;--i){
33 if(s[i]=='.') break;
34 c[++n]=s[i]-'0';
35 }
36 if(a-b>0) ans=(a-b)*n;
37 else ans=a-b;
38 gtnxt();
39 cout<<ans<<endl;
40 /*for(int i=1;i<=n;++i) cout<<c[i]<<" ";
41 cout<<endl;
42 for(int i=1;i<=n;++i) cout<<nxt[i]<<" ";
43 cout<<endl;*/
44 }
45 return 0;
46 }
