回文树——BZOJ 2160: 拉拉队排练

2160: 拉拉队排练

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 933   Solved: 365
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

5 3
ababa

Sample Output

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

代码:

#include<cstdio>    
#include<iostream>    
#include<cstring> 
#include<algorithm>   
#define CLEAR(xxx) memset(xxx, 0, sizeof(xxx)) 
#define LL long long   
using namespace std;    
const int maxn=1000000+5,mod=19930726;  
 
struct node{    
    int len,suffix;    
    LL Next[26];     
};   
struct Pair{     //排序用 
	int len,cnt;
	bool operator < (const Pair s)const {
		return len>s.len;
	}
}group[maxn]; 
node tree[maxn];    
int tot,len,max_suffix;       
char s[maxn],S[maxn];    
LL n,m,k,cnt[maxn],ans=1;    //cnt[i]表示i号节点代表的回文串出现的次数 
 
LL pow_mod(LL a,LL b,LL t){  //快速幂 
    LL ans=1;  
    for(a%=t;b;a=a*a%t,b>>=1)  
        if(b&1)ans=ans*a%t;  
    return ans;  
}      
  
void init(){    
    //初始化,添加两个节点。    
    for(int i=1;i<=n;i++){    
        tree[i].len=tree[i].suffix=0;    
        CLEAR(tree[i].Next);    
    }    
    memset(s,0,sizeof(s));    
    tot=max_suffix=2; len=0;    
    tree[1].len=-1;   
    tree[2].len=0;     
    tree[2].suffix=tree[1].suffix=1;    
}   
  
int Match(LL x){    
    while(s[len]!=s[len-tree[x].len-1]) x=tree[x].suffix;  
    return x;  
}  
  
void add_letter(char x){    
    s[++len]=x;    
    int cur,id=x-'a';    
    cur=Match(max_suffix);     
    if(tree[cur].Next[id]){    
    	int o= tree[cur].Next[id];
    	max_suffix=o; cnt[o]++;
		return ;  
    }         
    max_suffix=tree[cur].Next[id]=++ tot;    
    tree[tot].len=tree[cur].len+2;     
    if(tree[tot].len==1)   
        tree[tot].suffix=2;
	else { 
    	cur=Match(tree[cur].suffix);     
    	tree[tot].suffix=tree[cur].Next[id]; 
	}
	cnt[tot]++;
} 

void Count(){
	for(int i=tot;i>=0;i--)cnt[tree[i].suffix]+=cnt[i];	
	//完整统计,注意,建树时cnt[i]只记录了i号节点代表的回文串作为最长后缀时出现的次数 
}   

int main(){    
	  int i;
  	  cin>>n>>k;
  	  scanf("%s",S+1);
  	  init();
  	  for(i=1;i<=n;i++)add_letter(S[i]);
  	  Count();
  	  for(i=tot;i>=0;i--)
  	  	if(tree[i].len&1){
  	  		group[++m].len=tree[i].len;
  	  		group[m].cnt=cnt[i];
		}
	   sort(group+1,group+1+m);
	   for(i=1;i<=m&&k>0;i++){
	   		LL p=group[i].cnt;
	   		if(k>=p) ans=ans*pow_mod( (LL)group[i].len,p,mod)%mod;
	   		else ans=ans*pow_mod(group[i].len,k,mod)%mod; 
	   		k-=min(k,p);
	   }
	   if(k>0) puts("-1");
	   else cout<<ans%mod<<endl;
	   //for(i=1;i<=m;i++)cout<<group[i].len<<" "<<group[i].cnt<<endl;
}    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值