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

https://www.luogu.org/problemnew/show/P1659

题目描述

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

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

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

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

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

输入输出格式

输入格式:

输入为标准输入。

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

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

输出格式:

输出为标准输出。

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

思路

这是一道manacher经典例题。

由于我们只用求奇数回文串,所以中间可以不插字符。

这里给一个例子:abcba
这里是一个以c为中心长度为5的回文串。
不难发现,此串包含abcba,bcb,c的回文串,且都已c为中心。

所以我们一起使用前缀和来统计有多少个回文串。

由于数据很大,要用快速幂。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int mod=19930726,maxn=1000077;
long long len[maxn*2],n,k,a[maxn*2];
char s[maxn],st[maxn*2];
int init()
{
    scanf("%d%d%s",&n,&k,s);
    int l=strlen(s);
    int t=0;
    st[t]='$';
    for(int i=0; i<l; i++)
    {
        st[++t]=s[i];
    }
    st[t+1]='*';
    return t;
}
void manacher(int l)
{
    long long mx=0,rd=0;
    for(int i=1; i<=l; i++)
    {
        if(i<mx) len[i]=min(mx-i,len[2*rd-i]);
        else len[i]=1;
        while(st[len[i]+i]==st[i-len[i]]) 
        len[i]++;
        if(len[i]+i-1>mx)
        {
            mx=len[i]+i-1;
            rd=i;
        }
    }
}
long long power(int x,int y)
{
    long long t=x,b=y,ass=1;
    while(t)
    {
        if(t%2==1) ass=ass*b%mod;
        b=b*b%mod;
        t>>=1;
    }
    return ass;
}
long long sum(int l)
{
    long long mx=0,ass=1;
    for(int i=1; i<=l; i++) a[len[i]*2-1]++,mx=max(mx,len[i]*2-1);
    for(int i=mx-2; i>=1; i--) a[i]+=a[i+2];
    while(mx>0&&k>0)
    {
        int x=0;
        if(a[mx]<=k) x=a[mx];else x=k;
        ass=ass*power(x,mx)%mod;
        mx--; k-=x; 
    }
    if(k) return -1;else return ass;
}
int main()
{
    int l=init();
    manacher(l);
    printf("%d",sum(l));    
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值