[国家集训队]拉拉队排练
题面:
一句话题意:找前k大回文串(不要求本质不同)
题解:
我们进行一遍manacher即可求出对于每个回文中心而言的最长回文半径。
我们考虑求出f[i]表示回文半径为i的回文串的个数。
那么对于一个i而言,它可以对哪些长回文半径产生贡献呢? [1, r[i]]。(这个应该是很明显的)
因此每次就相当于区间加1,最后统计一次,用差分维护即可。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define mod 19930726 5 #define AC 1000100 6 #define LL long long 7 8 int n, pos, maxn, cnt; 9 int r[AC], d[AC], num[AC]; 10 LL k, ans = 1; 11 char s[AC]; 12 13 void pre() 14 { 15 scanf("%d%lld", &n, &k); 16 scanf("%s", s + 1); 17 } 18 19 void manacher() 20 { 21 s[0] = '#'; 22 for(R i = 1; i <= n; i ++) 23 { 24 r[i] = maxn > i ? min(r[2 * pos - i], maxn - i + 1) : 1; 25 while(s[i - r[i]] == s[i + r[i]]) ++ r[i]; 26 if(r[i] + i - 1 > maxn) maxn = r[i] + i - 1, pos = i;//maxn赋值错了.... 27 ++ d[1], -- d[r[i] + 1]; 28 } 29 } 30 31 inline bool cmp(int a, int b){ 32 return a > b; 33 } 34 35 inline void up(LL &a, LL b) 36 { 37 a *= b; 38 if(a > mod) a %= mod; 39 } 40 41 inline LL qpow(LL x, int have) 42 { 43 LL rnt = 1; 44 while(have) 45 { 46 if(have & 1) up(rnt, x); 47 up(x, x), have >>= 1; 48 } 49 return rnt; 50 } 51 52 void work() 53 { 54 for(R i = 1; i <= n; i ++) d[i] += d[i - 1]; 55 int t = n / 2 + 1; 56 for(R i = t; i; i --) 57 { 58 if(!d[i]) continue; 59 if(d[i] >= k) up(ans, qpow(i * 2 - 1, k)), k = 0; 60 else up(ans, qpow(i * 2 - 1, d[i])), k -= d[i]; 61 if(!k) break; 62 } 63 if(k) printf("-1\n"); 64 else printf("%lld\n", ans); 65 } 66 67 int main() 68 { 69 //freopen("in.in", "r", stdin); 70 pre(); 71 manacher(); 72 work(); 73 // fclose(stdin); 74 return 0; 75 }