• 【2019 CCPC 秦皇岛】J


    原题:

     

    题意:

    给你两个整数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 }
    View Code
  • 相关阅读:
    抽象类与接口的区别
    模板模式(Template Pattern)
    KVM虚拟化
    find文本处理(locate)实例学习记录
    AWK-文本处理测试实例记录
    Linux系统中如何查找大文件
    吞吐量和Iops、测试工具FIO使用
    linux了解
    了解docker
    语言资源国际化
  • 原文地址:https://www.cnblogs.com/cdcq/p/11678950.html
Copyright © 2020-2023  润新知