题目大概说给一个字符串,找到其所有子串[i...k]满足它是由两个回文串拼成的,求Σi*k。
官方题解这么说的:
用manacher算法O(n)求出所有的回文半径。有了回文半径后,就可以求出cntL[i]表示以i结尾的回文串的起始位置的和cntR[i]表示以i起始的回文串的结尾位置的和,然后就可以求出答案了,这里要注意奇偶长度回文串的不同处理。复杂度O(n)。
本渣渣看了好久想了好久。。才反应过来x1*y1+x1*y2+x2*y1+x2*y2=(x1+x2)*(y1+y2) 。。这个真反应不过来= =太渣了。。那么只要求出cntL[i]和cntR[i],Σ(cntL[i]*cntR[i+1])就是要的答案了。
本渣渣又看了好久想了好久。。才想到怎么在O(n)求出cntL[i]和cntR[i]。。
- 首先,跑一下Manacher就能知道每个位置向左和向右最多能延伸的长度
比如中间位置是i(回文串是奇数的情况,偶数同理。另外先不管跑Manacher前插入的特殊字符,因为也同理。。),p[i]表示Manacher求得的延伸半径。
- 那么[i-p[i]+1, i+p[i]-1]就是回文串,故cntL[i+p[i]-1]就该加上i-p[i]+1;
- 而[i-p[i]+2, i+p[i]-2]也是回文串,cntL[i+p[i]-2]就该加上i-p[i]+2…………
总之可以知道这个就相当于,对于cntL数组要在[i, i+p[i]-1]区间上依次加上一个末项为i-p[i]+1且公差为-1的等差数列,cntR也是同理的这儿同样就不说了。
- 那么现在问题就是怎么对区间更新,让区间[L,R]加上一个首项k公差-1的等差数列,这些更新操作完成后要进行单点的查询,而且整个的时间复杂度要为O(n)。
区间更新自然就联想到延迟标记。即,更新操作就用O(1)打打标记,所有更新操作完成后,从左往右O(n)遍历过去,把标记传递过去,并更新真实的值。
- 那么打什么标记?
首先能想到有一个加标记sumtag,表示这个数需要加多少。对于让区间[L,R]加上一个首项k公差-1的等差数列,就让sumtag[L]+=k,然后传递标记中访问到L时把sumtag[L]加到真实的值里,并把标记下传,即sumtag[L+1]+=sumtag[L]-1。不过考虑到多个区间修改有叠加,那么应该还要开几个数组记录各个点有多少个更新操作,sumcnt,那么下传也就要改成sumtan[L+1]+=sumtag[L]-sumcnt[L],另外sumcnt也要传递下去,即sumcnt[L+1]+=sum[cnt]。
不过这样还不够,因为这么传递会一直传递到数组尾巴,也就是说相当于更新了[L,MAXN]区间,而我们要的是[L,R]区间的更新,多更新了[R+1,MAXN]这个区间,而这个区间显然也是加上了一个等差数列!所以,考虑再用一个标记,减标记subtag,表示区间需要减的数,对于多更新的只要在subtag[R+1]这儿打上个标记就OK了!这个与sumtag是一样的,最后遍历标记下传的同时减去subtag的值即可,当然也需要subcnt。
于是这题就能这么解决了。。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define MAXN 1111111 6 7 char s[MAXN<<1]; 8 int p[MAXN<<1]; 9 void manacher(){ 10 int mx=0,id; 11 for(int i=1;s[i];++i){ 12 if(mx>i) p[i]=min(p[2*id-i],mx-i); 13 else p[i]=1; 14 for(;s[i+p[i]]==s[i-p[i]];++p[i]); 15 if(p[i]+i>mx){ 16 mx=p[i]+i; 17 id=i; 18 } 19 } 20 } 21 22 char str[MAXN]; 23 24 long long val[2][MAXN]; 25 long long addtag[2][MAXN],addcnt[2][MAXN],subtag[2][MAXN],subcnt[2][MAXN]; 26 void update(int flag,int l,int r,int k){ 27 addtag[flag][l]+=k; addtag[flag][l]%=1000000007; 28 ++addcnt[flag][l]; 29 subtag[flag][r+1]+=k-r+l; subtag[flag][r+1]%=1000000007; 30 ++subcnt[flag][r]; 31 } 32 void pushdown(){ 33 for(int i=1; str[i]; ++i){ 34 if(addcnt[0][i]){ 35 val[0][i]+=addtag[0][i]; val[0][i]%=1000000007; 36 addtag[0][i+1]+=addtag[0][i]-addcnt[0][i]; addtag[0][i+1]%=1000000007; 37 addcnt[0][i+1]+=addcnt[0][i]; 38 } 39 if(subcnt[0][i]){ 40 val[0][i]-=subtag[0][i]; val[0][i]%=1000000007; 41 subtag[0][i+1]+=subtag[0][i]-subcnt[0][i]; subtag[0][i+1]%=1000000007; 42 subcnt[0][i+1]+=subcnt[0][i]; 43 } 44 if(addcnt[1][i]){ 45 val[1][i]+=addtag[1][i]; val[1][i]%=1000000007; 46 addtag[1][i+1]+=addtag[1][i]-addcnt[1][i]; addtag[1][i+1]%=1000000007; 47 addcnt[1][i+1]+=addcnt[1][i]; 48 } 49 if(subcnt[1][i]){ 50 val[1][i]-=subtag[1][i]; val[1][i]%=1000000007; 51 subtag[1][i+1]+=subtag[1][i]-subcnt[1][i]; subtag[1][i+1]%=1000000007; 52 subcnt[1][i+1]+=subcnt[1][i]; 53 } 54 } 55 } 56 57 int main(){ 58 while(~scanf("%s",str+1)){ 59 s[0]=‘$‘; 60 int i=1; 61 for(;str[i];++i){ 62 s[(i<<1)-1]=‘#‘; 63 s[i<<1]=str[i]; 64 } 65 s[(i<<1)-1]=‘#‘; 66 s[i<<1]=0; 67 manacher(); 68 69 memset(val,0,sizeof(val)); 70 memset(addtag,0,sizeof(addtag)); 71 memset(addcnt,0,sizeof(addcnt)); 72 memset(subtag,0,sizeof(subtag)); 73 memset(subcnt,0,sizeof(subcnt)); 74 75 for(int i=1; s[i]; ++i){ 76 if(i&1){ 77 if(p[i]/2==0) continue; 78 update(0,i/2-p[i]/2+1,i/2,i/2+p[i]/2); 79 update(1,i/2+1,i/2+p[i]/2,i/2); 80 }else{ 81 update(0,i/2-p[i]/2+1,i/2,i/2+p[i]/2-1); 82 update(1,i/2,i/2+p[i]/2-1,i/2); 83 } 84 } 85 pushdown(); 86 87 long long ans=0; 88 for(int i=1; str[i]&&str[i+1]; ++i){ 89 ans+=val[1][i]*val[0][i+1]; 90 ans%=1000000007; 91 } 92 if(ans<0) ans+=1000000007; 93 printf("%I64d\n",ans); 94 } 95 return 0; 96 }