HDU5785 Interesting(manacher+差分陣列)
阿新 • • 發佈:2018-12-18
題意:一個串s,如果存在1<=i<=j<k<=|s|,且s[i,..j]和s[j+1,..k]都是迴文串,那麼對結果的貢獻就是i*k。求所有貢獻之和。
思路:對於每個位置i,設以i為末尾的迴文串的起點分別為s1,s2,...,以i+1為起點的迴文串的終點位置分別為t1,t2,....,那麼i位置對結果的貢獻=s1 * t1 + s1 * t2 +...+ s2 * t1 + s2 * t2 + ... = s1 * (t1 + t2 + ...) + s2 * (t1 + t2 + ...) + ... = (s1 + s2 + ...) * (t1 + t2 +...)。
因此處理出以每個位置i為終點的迴文串的起點位置之和,記為sum1[i],以i為起點的迴文串的集合的終點位置之和,記為sum2[i]。
先用manacher處理出每個點的迴文半徑,每個迴文串對迴文半徑範圍內都會產生貢獻,先累加每個迴文串對範圍內的貢獻,再區間求和即可。因為這裡RMQ的更新和查詢是分離的,所以可以用差分陣列來寫。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <stack> #include <cmath> #include <list> #include <cstdlib> #include <set> #include <map> #include <vector> #include <string> using namespace std; typedef long long ll; const ll linf = 0x3f3f3f3f3f3f3f3f; const int inf = 0x3f3f3f3f; const int maxn = 1000005; const int mod = 1000000007; const int N = 26; // 字符集大小 int n; int manacher(char s[], char str[], int p[]){ int len = n; str[0] = '*';//第一位是*,最後一位是'\0',防止下面的while越界 for(int i = 0; i <= len; ++i){//初始化 str[i * 2 + 1] = '#'; str[i * 2 + 2] = s[i]; } len = 2 * len + 1; int pos = 0, maxlen = 1; p[0] = 1; for(int i = 2; i < len; ++i){ if(i < p[pos] + pos){ p[i] = min(pos + p[pos] - i, p[pos * 2 - i]); } else { p[i] = 1; } while(str[i - p[i]] == str[i + p[i]]){ ++p[i]; } if(i + p[i] > pos + p[pos]){ pos = i;//更新位置 } if(p[i] > maxlen){ maxlen = p[i];//更新結果 } } return maxlen - 1; } char s[maxn], str[maxn * 2]; int p[maxn * 2]; ll cf1[maxn], cf2[maxn], sum1[maxn], sum2[maxn]; ll c1[maxn], c2[maxn], cnt1[maxn], cnt2[maxn]; // c1和c2記錄位置i被迴文串貢獻的次數 ll qz1[maxn], qz2[maxn]; int main() { while (~scanf("%s", s)) { n = strlen(s); for (int i = 0; i <= n + 1; ++i) { cf1[i] = cf2[i] = sum1[i] = sum2[i] = 0; c1[i] = c2[i] = cnt1[i] = cnt2[i] = 0; } manacher(s, str, p); int l = n << 1; for (int i = 2, r, j; i <= l; ++i) { r = p[i] >> 1;// 當前迴文串半徑 j = i >> 1; //當前字元的實際下標 if ((i & 1) && p[i] > 1) {// 處理偶數長度迴文串 //區間[j + 1, j + r]加上i cf1[j + 1] += i; cf1[j + r + 1] -= i; c1[j + 1] += 1; c1[j + r + 1] -= 1; //區間[j - r + 1, j] 加上i cf2[j - r + 1] += i; cf2[j + 1] -= i; c2[j - r + 1] += 1; c2[j + 1] -= 1; } else if (((i ^ 1) & 1)) {// 處理奇數長度迴文串 //區間[j, j + r - 1]加上i cf1[j] += i; cf1[j + r] -= i; c1[j] += 1; c1[j + r] -= 1; //區間[j - r + 1, j] 加上i cf2[j - r + 1] += i; cf2[j + 1] -= i; c2[j - r + 1] += 1; c2[j + 1] -= 1; } } for (int i = 1; i <= n; ++i) { sum1[i] = ((sum1[i - 1] + cf1[i] % mod + mod) % mod + mod) % mod; sum2[i] = ((sum2[i - 1] + cf2[i] % mod + mod) % mod + mod) % mod; cnt1[i] = ((cnt1[i - 1] + c1[i]) % mod + mod) % mod; cnt2[i] = ((cnt2[i - 1] + c2[i]) % mod + mod) % mod; } for (int i = 1; i <= n; ++i) { sum1[i] = (sum1[i] - cnt1[i] * i % mod + mod + mod) % mod; sum2[i] = (sum2[i] - cnt2[i] * i % mod + mod + mod) % mod; } ll ans = 0; for (int i = 1; i <= n; ++i) { ans = (ans + sum1[i] * sum2[i + 1] % mod) % mod; } printf("%lld\n", ans); } return 0; }