bzoj2160: 拉拉隊排練(manacher)
題目描述:艾利斯頓商學院籃球隊要參加一年一度的市籃球比賽了。拉拉隊是籃球比賽的一個看點,好的拉拉隊往往能幫助球隊增加士氣,贏得最終的比賽。所以作為拉拉隊隊長的楚雨蕁同學知道,幫助籃球隊訓練好拉拉隊有多麼的重要。拉拉隊的選拔工作已經結束,在雨蕁和校長的挑選下,n位集優秀的身材、舞技於一體的美女從眾多報名的女生中脫穎而出。這些女生將隨著籃球隊的小夥子們一起,和對手抗衡,為艾利斯頓籃球隊加油助威。一個陽光明媚的早晨,雨蕁帶領拉拉隊的隊員們開始了排練。n個女生從左到右排成一行,每個人手中都舉了一個寫有26個小寫字母中的某一個的牌子,在比賽的時候揮舞,為小夥子們吶喊、加油。雨蕁發現,如果連續的一段女生,有奇數個,並且他們手中的牌子所寫的字母,從左到右和從右到左讀起來一樣,那麼這一段女生就被稱作和諧小群體。現在雨蕁想找出所有和諧小群體,並且按照女生的個數降序排序之後,前K個和諧小群體的女生個數的乘積是多少。由於答案可能很大,雨蕁只要你告訴她,答案除以19930726的餘數是多少就行了。
輸入格式:輸入為標準輸入。第一行為兩個正整數n和K,代表的東西在題目描述中已經敘述。接下來一行為n個字元,代表從左到右女生拿的牌子上寫的字母。
輸出格式:輸出為標準輸出。輸出一個整數,代表題目描述中所寫的乘積除以19930726的餘數,如果總的和諧小群體個數小於K,輸出一個整數-1。
樣例輸出:
5 3
ababa
樣例輸出:
45
【樣例說明】
和諧小群體女生所拿牌子上寫的字母從左到右按照女生個數降序排序後為ababa, aba, aba, bab, a, a, a, b, b,前三個長度的乘積為。
解析:一道比較簡單的題。
看到迴文串和n的範圍,就可以想到用manacher。
注意這題只需要求出長度為奇數的迴文串即可,所以在進行manacher時不用多新增字元。
由於k的範圍較大,所以肯定不能單純拿出k個乘起來。
考慮到每個數都會對後面的數產生貢獻,所以可以維護一個類似字首和的東西,再用快速冪優化。
程式碼如下:
#include<cstdio> #include<algorithm> #define ll long long using namespace std; const int MOD = 19930726; const int maxn = 1e6 + 5; int n, mr, mid, hw[maxn]; ll bt[maxn], k; char s[maxn]; void manacher(void) { for (int i = 1; i <= n; ++ i) { if (i < mr) hw[i] = min(hw[mid * 2 - i], mr - i); else hw[i] = 1; while (i - hw[i] >= 0 && i + hw[i] <= n && s[i - hw[i]] == s[i + hw[i]]) hw[i] ++; if (i + hw[i] - 1 > mr) { mr = i + hw[i] - 1; mid = i; } } } int ksm(int x, ll y) { int res = 1, base = x; while (y > 0) { if (y & 1) res = 1ll * res * base % MOD; base = 1ll * base * base % MOD; y >>= 1; } return res; } int main() { scanf("%d %lld", &n, &k); scanf("%s", s + 1); manacher(); for (int i = 1; i <= n; ++ i) bt[hw[i] * 2 - 1] ++; ll ans = 1, sum = 0; if (!(n & 1)) n --; for (int i = n; i > 0 ; i -= 2) { sum += bt[i]; if (k < sum) { ans *= ksm(i, k); ans %= MOD; break; } else ans *= ksm(i, sum); ans %= MOD; k -= sum; } if (sum < k) return puts("-1"), 0; printf("%lld", ans); return 0; }