HDU5785 Interesting(Manacher + 延迟标记)

题目大概说给一个字符串,找到其所有子串[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求得的延伸半径。

  1. 那么[i-p[i]+1, i+p[i]-1]就是回文串,故cntL[i+p[i]-1]就该加上i-p[i]+1;
  2. 而[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 }
时间: 2024-10-09 08:58:35

HDU5785 Interesting(Manacher + 延迟标记)的相关文章

HDU 5785 Interesting manacher + 延迟标记

题意:给你一个串,若里面有两个相邻的没有交集的回文串的话,设为S[i...j] 和 S[j+1...k],对答案的贡献是i*k,就是左端点的值乘上右端点的值. 首先,如果s[x1....j].s[x2....j].s[x3....j]....s[xn....j].是回文串,而且s[j+1...y1].s[j+1...y2].s[j+1...y3].也是回文串,那么这些串对答案的贡献一共就是(x1+x2+...+xn)*(y1+y2+....+yn) 所以想到了用cntL[i]表示:以i这个字符为

多校1005 HDU5785 Interesting (manacher)

1 // 多校1005 HDU5785 Interesting 2 // 题意:给你一个串,求相邻两个回文串左边端点*右边端点的和 3 // 思路:马拉车算出最长回文半径,求一个前缀和,既得到每个点对答案的贡献. 4 // ans=L[i]*R[i-1] 5 // L[i] 以i开始的所有回文串结尾坐标的和 6 // R[i] 以i结尾的所有回文串开始坐标的和 7 // 这题我们关键是求L[] R[] 8 // 另开两个数组a[] b[] 分别记录当前点对答案贡献 和每个点出现的次数 9 10

[uva11992]Fast Matrix Operations(多延迟标记,二维线段树,区间更新)

题目链接:https://vjudge.net/problem/UVA-11992 题意:n*m的矩阵,每次对一个子矩阵操作,有三种操作:加x,设置为x,查询.查询返回子矩阵和.最小值.最大值 n很小(<=20),所以可以开20棵线段树,每次操作按行更新. 特别小心put和add两个延迟标记,坑老惨了. put初始化-1最简单的坑,略过. build的时候要每一个节点都要clear,不能只clear叶子.因为会有直接差没操作的子矩阵(因为初始化都是0). 数组开大... add的话,什么都不用管

HDU 1698 Just a Hook (区间更新+延迟标记)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 31096    Accepted Submission(s): 15313 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible thing

Codeforces Round #262 (Div. 2)C(二分答案,延迟标记)

这是最大化最小值的一类问题,这类问题通常用二分法枚举答案就行了. 二分答案时,先确定答案肯定在哪个区间内.然后二分判断,关键在于怎么判断每次枚举的这个答案行不行. 我是用a[i]数组表示初始时花的高度,b[i]表示要达到当前枚举的答案(即mid的值)需要这朵花再涨多少.这两个数组很好算,关键是一次浇连续的w朵花,如何更新区间(暴力的O(n2)的去更新就超时了)?可以用线段树,但是这道题没有涉及区间查询,就是在一个数组上更新区间,用线段树未免小题大做.那么其实这种更新就用延迟标记的思想(懒操作)就

E - Just a Hook HDU 1698 (线段树+类似延迟标记)

E - Just a Hook Time Limit:2000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Practice Description In the game of DotA, Pudge's meat hook is actually the most horrible thing for most of the heroes. The hook is made up of several

浅析__线段树延迟标记

转载请注明出处:http://blog.csdn.net/u012860063?viewmode=contents 当中有文字系转载! 区间更新是指更新某个区间内的叶子节点的值,由于涉及到的叶子节点不止一个,而叶子节点会影响其对应的非叶父节点,那么回溯须要更新的非叶子节点也会有非常多,假设一次性更新完,操作的时间复杂度肯定不是O(lgn),比如当我们要更新区间[0,3]内的叶子节点时,须要更新除了叶子节点3,9外的全部其它节点.为此引入了线段树中的延迟标记概念,这也是线段树的精华所在. 延迟标记

HDU 1890 Robotic Sort 伸展树的区间反转与延迟标记

延迟标记像极了线段树,不再多说. 区间反转在树伸展到位之后,也变成了简单的递归交换左右儿子. 愈发感觉到伸展树简直太漂亮了,伸展操作更是诱惑到不行 ,总之数据结构太有魅力了. 比较简单,就直接上模板了. #include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #in

杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)

欢迎"热爱编程"的高考少年--报考杭州电子科技大学计算机学院 Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 20889    Accepted Submission(s): 10445 Problem Description In the game of DotA, Pudge's meat hook