$[ CF 17 E ] Palisection$
阿新 • • 發佈:2018-09-22
getchar gist 包含著 type rip nac += manacher i+1
\(\\?\)
\(Description\)
給定一個長度為\(N\)的小寫字母串。問有多少對相交的回文子串\((\)包含也算相交\()\),答案對\(51123987\)取模。
- \(N\in [1,2\times 10^6]\)
\(\\\)
\(Solution\)
先考慮相交如何處理。因為相交既跟端點有關,又跟長度有關,回文子串數目眾多不好處理。正難則反。假設總回文串個數是\(cnt\)個,如果兩兩都相交一共有\(\frac{cnt\times(cnt-1)}{2}\)對,所以答案就是\(\frac{cnt\times(cnt-1)}{2}-\)不相交回文子串對數。
先考慮處理回文串個數回文串個數。在\(Manacher\)
在考慮處理不相交對數。統計以每一個位置為左端點和右端點的回文子串個數,根據端點處理。一個直接的想法是對每一個位置直接累加,右端點在它左邊的子串數\(\times\)左端點在它右邊的子串數,但這種方法顯然會算重很多部分。考慮固定一側,即確定左端點必須在這一位置,讓這個子串數乘上右端點在它左邊的子串數累加出來的答案,是補充不漏的,其中第二項做的時候可以前綴和處理。
關於以每一個開始和結束的回文子串數量,每求出一個位置暴力累加所有子串復雜度顯然過不了。考慮差分。因為新加入的子串開頭的位置一定是一段連續的區間,從最左的端點一直到回文中心,而結束的也是同理。最後對兩個數列分別求一下前綴和就好,註意跟上一條結合在一起,右端點的數列會在計算過程中求兩遍前綴和,即最後得到的是原數列的前綴和。
\(\\\)
\(Code\)
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 2000010 #define R register #define gc getchar #define mod 51123987 using namespace std; typedef long long ll; char c,s[N<<1]; ll ans; int n,p,mr,slen,l[N<<1],r[N<<1],len[N<<1]; inline void init(){ scanf("%d",&n); while(!isalpha(c=gc())); s[1]=s[slen=3]='#'; s[2]=c; for(R int i=2;i<=n;++i){s[++slen]=gc();s[++slen]='#';} s[0]='['; s[slen+1]=']'; } inline void manacher(){ for(R int i=1,p=0,mr=0;i<=slen;++i){ len[i]=(i>mr)?1:min(mr-i+1,len[(p<<1)-i]); while(s[i-len[i]]==s[i+len[i]]) ++len[i]; if(i+len[i]-1>mr){mr=i+len[i]-1;p=i;} ++l[i-len[i]+1]; --l[i+1]; ++r[i]; --r[i+len[i]]; (ans+=(ll)(len[i]>>1))%=mod; } ans=(ans*(ans-1)/2)%mod; } int main(){ init(); manacher(); for(R int i=1,sum=0;i<=slen;++i){ l[i]+=l[i-1]; r[i]+=r[i-1]; if(i%2==0){ ans=(ans-(ll)l[i]*sum%mod+mod)%mod; (sum+=r[i])%=mod; } } printf("%lld\n",ans); return 0; }
$[\ CF\ 17\ E\ ]\ Palisection$