【回文自动机】 BZOJ 2160 拉拉队排练
题意:求所有回文按长度排序后,前k个奇数长度的子串乘积之和。
思路:len[i]代表长度为i的有多少个子串,避免超时,树状数组维护即可
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int MAX_N = 1200005; const int SIG = 26 ; const int N = MAX_N; struct Bit { ll bit[N]; void clear() { memset(bit, 0, sizeof bit); } void add(ll i, ll v) { if (i == 0) bit[0] += v; else { for (;i < N; i += i & -i) bit[i] += v; } } ll query(int i) { if (i < 0) return 0; else { ll re = 0; for (;i > 0; i -= i & -i) re += bit[i]; return re; } } }; Bit B; struct PTree { int nxt[MAX_N][SIG], fail[MAX_N], num[MAX_N], S[MAX_N]; int last, n, p;ll cnt[MAX_N], len[MAX_N]; int newNode (int l) { memset(nxt[p], 0, sizeof nxt[p]); cnt[p] = num[p] = 0; len[p] = l; return p++; } void init() { p = last = n = 0; newNode(0), newNode(-1); S[n] = -1; fail[0] = 1; } int getFail(int x) { while (S[n - len[x] - 1] != S[n]) x = fail[x]; return x; } void add(int c) { c -= 'a'; S[++n] = c; int cur = getFail(last); if (!nxt[cur][c]) { int now = newNode(len[cur] + 2); fail[now] = nxt[getFail(fail[cur])][c]; nxt[cur][c] = now; num[now] = num[fail[now]] + 1; } last = nxt[cur][c]; ++cnt[last]; } void count() { for (int i = p - 1; i >= 0; --i) cnt[fail[i]] += cnt[i]; } }; const ll MOD = 19930726ll; PTree A; ll n, m, len[MAX_N]; char s[MAX_N]; ll Pow(ll x, ll n) { ll ret = 1; while (n > 0) { if (n & 1) ret = ret * x % MOD; x = x * x % MOD; n >>= 1; } return ret; } int main() { A.init(); scanf("%lld%lld%s", &n, &m, s); for (int i = 0; s[i]; ++i) A.add(s[i]); ll ans = 1; B.clear(); A.count(); for (int i = 2; i < A.p; ++i) { B.add(A.len[i], A.cnt[i]); } len[1] = B.query(1); for (int i = 2; i <= n; ++i) len[i] = B.query(i) - B.query(i - 1); for (int i = n; m && i > 0; --i) { if (i % 2 == 0 || len[i] == 0) continue; if (len[i] > m) { ans = (ans * Pow(i, m)) % MOD; m = 0; } else { ans = (ans * Pow(i, len[i])) % MOD; m -= len[i]; } } if (m > 0) ans = -1; printf("%lld\n", ans); return 0; }