题面:洛谷(带翻译)
题解:
直接求相交不太好求,所以考虑求不相交的回文串对数。
设ll[i]表示以i为开头的回文串个数,rr[i]表示结尾<=i的回文串个数。
然后不相交的回文串对数显然就是对于每个$rr[i - 1] cdot ll[i]$求一次和。
最后再用全集减去不相交的回文串对数即可求出相交的回文串对数。
那么如何求这2个数组呢?
对于每个最长回文半径r[i],它可以对一个区间做出贡献,即以它为中心那些回文串的开头or结尾都在一个区间内,相当于每个r[i]都可以对某个区间做区间加。
因此我们差分维护一下可以得到ll数组,同理也可以得到表示以i为结尾的回文串个数的数组,然后对这个数组求一次前缀和即可得到rr数组
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define R register int 4 #define AC 2010000 5 #define ac 4001000 6 #define LL long long 7 #define p 51123987 8 9 int n, maxn, pos, len; 10 int r[ac]; 11 LL ll[AC], rr[AC]; 12 LL sum; 13 char ss[AC], s[ac]; 14 15 void pre() 16 { 17 scanf("%d%s", &len, ss + 1); 18 s[0] = '$', s[1] = '#', n = (len << 1) | 1; 19 for(R i = 1; i <= len; i ++) s[i << 1] = ss[i], s[(i << 1) | 1] = '#'; 20 } 21 22 void manacher() 23 { 24 for(R i = 1; i <= n; i ++) 25 { 26 r[i] = (maxn > i) ? min(r[(pos << 1) - i], maxn - i + 1) : 1; 27 while(s[i + r[i]] == s[i - r[i]]) ++ r[i]; 28 if(i + r[i] - 1 > maxn) maxn = i + r[i] - 1, pos = i; 29 ++ rr[(i + 1) >> 1], -- rr[(i + r[i]) >> 1]; 30 ++ ll[(i - r[i] + 1 + 1) >> 1], -- ll[(i >> 1) + 1]; 31 } 32 for(R i = 1; i <= len; i ++) 33 rr[i] = (rr[i] + rr[i - 1]) % p, ll[i] = (ll[i] + ll[i - 1]) % p; 34 for(R i = 1; i <= len; i ++) rr[i] = (rr[i] + rr[i - 1]) % p;//统计一次前缀和 35 } 36 37 void work() 38 { 39 sum = rr[len] * (rr[len] - 1) / 2 % p; 40 for(R i = 2; i <= len; i ++) 41 sum = (sum - rr[i - 1] * ll[i] % p + p) % p; 42 printf("%lld ", (sum + p) % p); 43 } 44 45 int main() 46 { 47 // freopen("in.in", "r", stdin); 48 pre(); 49 manacher(); 50 work(); 51 // fclose(stdin); 52 return 0; 53 }