BZOJ 2160: 拉拉队排练(回文树)

 

传送门:

  [1]:BZOJ

  [2]:洛谷

 

•题意

  求串 s 中出现的所有奇回文串,并按照长度由大到小排序;

  输出前 k 个奇回文串的乘积 mod 19930726;

  如果奇回文串的个数不足 k 个,输出 -1;

•题解

  将串 s 跑一边回文自动机;

  将求解出的 len,cnt 数组存入一个结构体中并按照 len 由大到小排序;

  将前 k 个奇回文串的长度相乘就行;

  记得用快速幂,并且只要奇回文串的长度乘积;

•Code

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define ll long long
  4 const int MOD=19930726;
  5 const int maxn=1e6+50;
  6  
  7 int n;
  8 ll k;
  9 char s[maxn];
 10  
 11 struct PAM
 12 {
 13     int tot;
 14     int last;
 15     ll cnt[maxn];
 16     ll len[maxn];
 17     int fail[maxn];
 18     int son[maxn][30];
 19  
 20     int newNode(int Len)
 21     {
 22         for(int i=0;i < 30;++i)
 23             son[tot][i]=0;
 24  
 25         len[tot]=Len;
 26         fail[tot]=0;
 27         cnt[tot]=0;
 28  
 29         return tot++;
 30     }
 31     int getFail(int p,int i)
 32     {
 33         while(s[i-len[p]-1] != s[i])
 34             p=fail[p];
 35         return p;
 36     }
 37     void Init()
 38     {
 39         tot=0;
 40         last=0;
 41  
 42         newNode(0);
 43         newNode(-1);
 44  
 45         fail[0]=1;
 46     }
 47     void Count()
 48     {
 49         for(int i=tot-1;i >= 0;--i)
 50             cnt[fail[i]] += cnt[i];
 51     }
 52     void pam()
 53     {
 54         Init();
 55  
 56         for(int i=1;i <= n;++i)
 57         {
 58             s[i]=s[i]-'a'+1;
 59  
 60             int cur=getFail(last,i);
 61  
 62             if(!son[cur][s[i]])
 63             {
 64                 int now=newNode(len[cur]+2);
 65                 fail[now]=son[getFail(fail[cur],i)][s[i]];
 66                 son[cur][s[i]]=now;
 67  
 68             }
 69             cnt[last=son[cur][s[i]]]++;
 70         }
 71         Count();
 72     }
 73 }_pam;
 74 struct Data
 75 {
 76     ll cnt;
 77     ll len;
 78     bool operator < (const Data &obj) const
 79     {
 80         return len > obj.len;
 81     }
 82 }a[maxn];
 83  
 84 ll qPow(ll a,ll b,ll m)
 85 {
 86     ll ans=1;
 87     a %= m;
 88     while(b)
 89     {
 90         if(b&1)
 91             ans=ans*a%m;
 92         a=a*a%m;
 93         b >>= 1;
 94     }
 95     return ans;
 96 }
 97 int main()
 98 {
 99     scanf("%d%lld",&n,&k);
100     scanf("%s",s+1);
101     s[0]='#';
102  
103     _pam.pam();
104  
105     int x=0;
106     for(int i=2;i < _pam.tot;i++)
107         a[++x]=Data{_pam.cnt[i],_pam.len[i]};
108  
109     sort(a+1,a+x+1);
110  
111     int index=1;
112     ll ans=1;
113     while(index <= x)
114     {
115         if(!(a[index].len&1))///坑:如果不是奇回文串,continue
116         {
117             index++;
118             continue;
119         }
120  
121         ll cur=min(k,a[index].cnt);
122  
123         ans *= qPow(a[index++].len,cur,MOD);
124         ans %= MOD;
125         k -= cur;
126  
127         if(k == 0)
128             break;
129     }
130     if(k > 0)
131         ans=-1;
132  
133     printf("%lld\n",ans);
134 }
View Code

 

转载于:https://www.cnblogs.com/violet-acmer/p/11275316.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值