P1659 [国家集训队]拉拉队排练

题目描述

艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。

拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。

一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。

雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。

现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。

输入输出格式

输入格式:

输入为标准输入。

第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。

接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。

输出格式:

输出为标准输出。

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

输入输出样例

输入样例#1: 
5 3
ababa
输出样例#1: 
45

说明

样例说明

和谐小群体女生所拿牌子上写的字母从左到右按照女生个数降序排序后为ababa, aba, aba, bab, a, a, a, b, b,前三个长度的乘积为 。

数据范围与约定

测试点nK
1 10 10
2-3 100 100
4-7 1,000 1,000
8 100,000 = 1
9-11 100,000 100,000
12-14 100,000 1,000,000,000,000
15-17 500,000 1,000,000,000,000
18 1,000,000 = 1
19 1,000,000 1,000,000
20 1,000,000 1,000,000,000,000

Solution:

  既然刚学了“马拉车”,于是打道题练手,发现洛谷除了模板就这一道manacher的题了,看来马拉车果然很冷门啊!

  闲话少说,说下思路。

  本题我们发现其实就是给定一个长度为n的字符串和一个k,求前k个长度为奇数的回文子串的长度乘积,不足k个就输出-1。

  算法就是manacher+快速幂,因为只需要长度为奇数的回文子串,于是不需要构造新串,在求每个中心的最长回文子串半径时,将出现过的半径值都用一个桶来统计,因为长度最长为n(≤1,000,000),所以不会有问题。输出时,从离n最近的奇数枚举到1出现的奇数,由于k很大而且相同的数可能有很多重复,于是对于每个桶中的数直接快速幂求乘积,然后相应的将k减小,知道已经累乘过的数的个数大于等于k就跳出循环。输出时判断一下就ok了。

代码:

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define debug printf("%d %s
",__LINE__,__FUNCTION__)
using namespace std;
const int N=1000050,mod=19930726;
ll p[N],sum,tot[N];
ll n,k,ans=1;
char s[N];
il ll fast(ll x,ll k)
{
    ll ans=1;
    while(k){
        if(k&1)ans=ans*x%mod;
        k>>=1;
        x=x*x%mod;
    }
    return ans;
}
il void manacher()
{
    int id,mx=-1;
    for(ll i=1;i<=n;i++){
        if(i<mx)p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while(s[i+p[i]]==s[i-p[i]])p[i]++;
        if(mx<i+p[i])mx=i+p[i],id=i;
        tot[2*p[i]-1]++;
    }
}
int main()
{
    scanf("%lld%lld%s",&n,&k,s+1);
    s[0]='$';
    manacher();
    //for(int i=1;i<=n;i++)cout<<tot[i];
    for(int i=n%2==0?n-1:n;i>=1;i-=2){
        sum+=tot[i];
        if(sum>k){ans=ans*fast(i,k)%mod;break;}
        else {ans=ans*fast(i,sum)%mod;k-=sum;}
    }
    if(sum<k){cout<<-1;return 0;}
    cout<<ans;
    return 0;
}
原文地址:https://www.cnblogs.com/five20/p/8616294.html