BZOJ 2160 Manacher 解题报告

来源:互联网 发布:软件编程师 编辑:程序博客网 时间:2024/06/08 23:41

2160: 拉拉队排练

Description

艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。

Input

输入为标准输入。第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。

Output

输出为标准输出。输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。

Sample Input

5 3
ababa

Sample Output

45

【解题报告】
题目大意是,给出一个长度为n的字符串,求最长的k个回文子串(此题中的回文子串指长为奇数的回文子串)长度的乘积。模19930726后输出,若没有k个则输出-1。(n<=10e6,k<=10e12)
所以首先可以用manacher算法确定每个位置的回文半径, 由于这里只需要长度是奇数, 所以不需要插入‘#’, 直接在首尾添加好不同字符之后跑一遍manacher算法。 对于位置i为中心的回文半径pal[i], 用dp[i]来表示相邻长度的回文串的数量差分(其实就是一个常用的前缀和技巧, 因为这里每次更新[1, pal[i]]这个区间 + 1, 而只在所有更新完毕之后才查询所以没有必要使用树状数组, 直接根据每次更新的时候dp[1]++, dp[R[i] + 1]–, 最后后dp[1~i]的和就是最终ans[i]的值, 即长度为2*i - 1的回文串的数量
然后用快速幂就可以了

代码如下:

/**************************************************************    Problem: 2160    User: onepointo    Language: C++    Result: Accepted    Time:548 ms    Memory:21328 kb****************************************************************/#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define N 1000010#define ll long long#define Mod 19930726char s[N];int n,pal[N];ll k,dp[N],ans[N];ll quick_pow(ll base,ll pow)  {      ll ret=1;      while(pow)      {          if(pow&1) ret=(ret*base)%Mod;          pow>>=1;base=base*base%Mod;      }      return ret;  }  void manacher(){    int len=n;    int id=0,mx=0;    s[0]='+',s[len+1]='-';    for(int i=1;i<=len;++i)    {        if(mx>i) pal[i]=min(mx-i,pal[id*2-i]);          else pal[i]=1;          while(s[i-pal[i]]==s[i+pal[i]]) ++pal[i];          if(mx<pal[i]+i) mx=pal[i]+i,id=i;        }   }int main(){    scanf("%d%lld",&n,&k);    scanf("%s",s+1);         manacher();      memset(dp,0,sizeof(dp));    memset(ans,0,sizeof(ans));      for(int i=1;i<=n;++i)     {          dp[1]++;          dp[pal[i]+1]--;      }        int maxlen=0;      for(int i=1;i<=(n+1)>>1;++i)    {          ans[i]=ans[i-1]+dp[i];          if(ans[i]>0) maxlen=i;      }      ll result=1;      int r=maxlen;      while(r&&k>0)      {        result=(result*quick_pow((ll)(2*r-1),min(ans[r],k)))%Mod;        k-=ans[r];--r;      }    (k>0)?printf("-1\n"):printf("%lld\n",result);    return 0;   }
原创粉丝点击