[manacher]拉拉队排练

题目描述

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

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

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

Sample Input
5 3
ababa

Sample Output
45

n≤10^6 k≤10^12

分析

求回文串,典型manacher
利用len数组的重合(大的包含以下所有奇数串)
可以跑一个前缀和,利用前缀和下标和数量跑快速幂得出结果(不要问为什么用快速幂)

#include <iostream>
#include <cstdio>
#define rep(i,a,b) for (i=a;i<=b;i++)
#define q 19930726
using namespace std;
long long len[1000003],a[1000003];
string s;
int n;
long long k;
void INIT()
{
    scanf("%d%lld\n",&n,&k);
    s="";
    s+='$';
    int i;
    char c;
    rep(i,1,n)
    {
        scanf("%c",&c);
        s+=c;
    }
    s+='#';
    n+=2;
}

void MANACHER(string s)
{
    int mx=0,i,j,id=0;
    rep(i,0,n-1)
    {
        j=id*2-i;
        if (i<mx)
        len[i]=min(len[j],(long long)mx-i);
        else len[i]=1;
        while (s[i-len[i]]==s[i+len[i]])
        len[i]++;
        if (i+len[i]>mx)
        {
            id=i;
            mx=i+len[i];
        }
    }
}

long long GET_POWER(long long x,long long r)
{
    long long ans=1;
    while (r)
    {
        if (r&1) ans=ans*x%q;
        x=x*x%q;
        r>>=1;
    }
    return ans;
}

long long GET_SUM()
{
    int i,mx=0;
    long long x;
    long long ans=1;
    rep(i,1,n-2)
    a[len[i]*2-1]++,mx=max((long long)mx,len[i]*2-1);
    for (i=mx-2;i>=1;i--)
    a[i]+=a[i+2];
    while(mx>0&&k>0)
    {
        x=a[mx]>k?k:a[mx];
        ans=ans*GET_POWER(mx,x)%q;
        mx--;k-=x;
    }
    return ans;
}

int main()
{
    INIT();
    MANACHER(s);
    printf("%lld",GET_SUM());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值