【bzoj2160】【啦啦队排练】manacher(马拉车)+差分+快速幂

这里写图片描述
[pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=34562780
向大(hei)佬(e)势力学(di)习(tou)

Description
艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了。拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛。所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多么的重要。拉拉队的选拔工作已经结束,在雨荨和校长的挑选下,n位集优秀的身材、舞技于一体的美女从众多报名的女生中脱颖而出。这些女生将随着篮球队的小伙子们一起,和对手抗衡,为艾利斯顿篮球队加油助威。一个阳光明媚的早晨,雨荨带领拉拉队的队员们开始了排练。n个女生从左到右排成一行,每个人手中都举了一个写有26个小写字母中的某一个的牌子,在比赛的时候挥舞,为小伙子们呐喊、加油。雨荨发现,如果连续的一段女生,有奇数个,并且他们手中的牌子所写的字母,从左到右和从右到左读起来一样,那么这一段女生就被称作和谐小群体。现在雨荨想找出所有和谐小群体,并且按照女生的个数降序排序之后,前K个和谐小群体的女生个数的乘积是多少。由于答案可能很大,雨荨只要你告诉她,答案除以19930726的余数是多少就行了。
Input
输入为标准输入。第一行为两个正整数n和K,代表的东西在题目描述中已经叙述。接下来一行为n个字符,代表从左到右女生拿的牌子上写的字母。
Output
输出为标准输出。输出一个整数,代表题目描述中所写的乘积除以19930726的余数,如果总的和谐小群体个数小于K,输出一个整数-1。
Sample Input
5
ababa
Sample Output
45
【样例说明】
和谐小群体女生所拿牌子上写的字母从左到右按照女生个数降序排序后为ababa, aba, aba, bab, a, a, a, b, b,前三个长度的乘积为。
HINT
总共20个测试点,数据范围满足: n<=10^6,k<=10^9

数据范围可知,要么o(n),要么o(nlogn)

首先,要求回文串,自然是想到manacher。题目也很良心,只要求你求奇数长的回文串,这样就可以直接对原串跑马拉车,也方便了之后的串长统计。

但是知道了每个节点的最长串长还不够,回文串还有包含关系,而其子串也有可能对答案做出贡献。我们想,如果能统计串长为x的有多少个就好了,长为x的串可以对的长度小于x的也做出贡献。

于是我们想到建一个值域数组,找到一个长为x的回文串,就对[1,x]的区间+1。下意识的想到线段树,但是大佬告诫我,不要什么都想着用线段树,普通的区间修改用差分也可以。

那么差分处理完区间加后,就是计算答案了。一看k的范围,暴力乘坑定TLE。但是实际上底数只会有最多10^5个,快速幂就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long 
using namespace std;

const ll N=1000000+5;
const ll mod=19930726;

ll n,k,pal[N];
char s[N];
ll a[N],sum[N],ans;

void manacher(){
    ll mx=0,id;
    for(int i=1;i<=n;i++){
        if(i<=mx) pal[i]=min(mx-i+1,pal[2*id-i]);
        else pal[i]=1;
        while(s[i+pal[i]]==s[i-pal[i]]) pal[i]++;
        if(i+pal[i]-1>mx)
            mx=i+pal[i]-1,id=i;
    }
}
ll pow(ll x,ll aa){
    ll rt=1;
    while(aa){
        if(aa&1) rt=(rt*x)%mod;
        x=(x*x)%mod;
        aa>>=1;
    }
    return rt;
}
int main(){
    scanf("%I64d%I64d",&n,&k);
    scanf("%s",s+1);
    s[0]='-',s[n+1]='+';
    manacher();
    for(int i=1;i<=n;i++)
        a[1]++,a[pal[i]*2]--;
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i];
    }
    ans=1;
    for(int i=n;i>=1;i--){
        if(i%2==0) continue;
        int p=min(k,sum[i]);
        k-=p;
        ans=(ans*pow(i,p))%mod;
    }
    if(k) printf("-1");
    else printf("%I64d",ans);
    return 0;
}

总结:
1、区间处理别总想线段树,数据结构多了又复杂又难写,能简则简

posted @ 2017-10-31 19:09  LinnBlanc  阅读(158)  评论(0编辑  收藏  举报