Time Limits: 1000 ms Memory Limits: 524288 KB
Description
Input
Output
Sample Input
6 2
ACACAA
1 3
3 6
Sample Output
4
6
Data Constraint
Solution
题意:
给出一个长度为
n
n
的母串
每次询问
S[l,r]
S
[
l
,
r
]
中回文子串的个数
思路:
先做Manacher,经过观察我们可以得到:
若
S[k]
S
[
k
]
为分隔符,则以
S[k]
S
[
k
]
为中点并且在原串中实际存在的回文串个数为
f[k]2
f
[
k
]
2
否则
S[k]
S
[
k
]
为字母,实际存在的回文串个数为
f[k]+12
f
[
k
]
+
1
2
对于原询问
[l,r]
[
l
,
r
]
,在新串中对应区间为
[2∗l−1,2∗r+1]
[
2
∗
l
−
1
,
2
∗
r
+
1
]
不妨令
L=2∗l−1,R=2∗r+1
L
=
2
∗
l
−
1
,
R
=
2
∗
r
+
1
考虑枚举回文串的中点,由于回文串的左/右端点不能超过L/R,可以得到
Ans=12[(r−l+1)+∑Rk=Lmin(f[k],k−L,R−k)] A n s = 1 2 [ ( r − l + 1 ) + ∑ k = L R m i n ( f [ k ] , k − L , R − k ) ]
可以分别考虑 k−L<=R−k k − L <= R − k 与 k−L>R−k k − L > R − k 的情况
情形1:当 k<=L+R2=l+r k <= L + R 2 = l + r 时,目标转化为求出 ∑l+rk=Lmin(f[k],k−L) ∑ k = L l + r m i n ( f [ k ] , k − L )
情形2:当 k>L+R2=l+r k > L + R 2 = l + r 时,做法类似
这是一个经典的问题,通过分类讨论去掉 min m i n 以后,问题转化为求二维平面矩形内点权和,据说可以使用可持久化线段树来维护这个二维点集,可以做到单次询问 O(logn) O ( l o g n ) ,当然,由于我很弱,所以我用的是扫描线
对于情形1:我们考虑从后往前做,由于 L L 在减小,对于一个 来说,左边界的减小会使它对答案的贡献增大,而当 L<k−f[k] L < k − f [ k ] 的时候,无论 L L 再如何左移, 由于原来以 为中心的最长字符串长度的限制, k k 位置对答案的贡献都将不再增加,所以我们每次对 后可以增加贡献的增加贡献,再将不可增加的打个标记,最后再求出答案即可
对于情形2:与情形1类似
时间复杂度 O((n+m)logn) O ( ( n + m ) l o g n )
Code
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<vector>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define ll long long
#define N 130010
using namespace std;
int n,m,i,opl,opr,opx;
int f[2*N],d[8*N],p[8*N],c[N][2];
ll sum,ans[N],s[8*N];
char a[2*N],b[2*N];
vector<int> L[2*N],R[2*N],S[2*N];
void read(int &n)
{
int x=0,w=0; char ch=0;
while(!isdigit(ch)) w|=ch=='-',ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
n=w?-x:x;
}
void write(ll x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int Min(int x,int y)
{
return x<y?x:y;
}
void Manacher()
{
int i,x=0,p=0;
fo(i,1,n) b[2*i-1]='#',b[2*i]=a[i];
n=2*n+1,b[n]='#',b[n+1]='@';
fo(i,1,n)
{
f[i]=x>i?Min(x-i,f[2*p-i]):1;
while(b[i-f[i]]==b[i+f[i]]) f[i]++;
if(f[i]+i>x) x=f[i]+i,p=i;
}
fo(i,1,n) f[i]--;
}
void down(int x,int i,int m,int j)
{
ll u=x+x,v=x+x+1;
s[u]+=(m-i+1-d[u])*p[x];
s[v]+=(j-m-d[v])*p[x];
p[u]+=p[x],p[v]+=p[x],p[x]=0;
}
void update(int x)
{
d[x]=d[x+x]+d[x+x+1];
s[x]=s[x+x]+s[x+x+1];
}
void calc(int x,int l,int r)
{
if(opl>opr||opl<1||opr>n) return;
if(opl<=l&&opr>=r)
{
if(opx==1) s[x]+=r-l+1-d[x],p[x]++;
else if(opx==2) sum+=s[x];
else s[x]=f[opl],d[x]=1,p[x]=0;
return;
}
int mid=(l+r)>>1; down(x,l,mid,r);
if(opl<=mid) calc(x+x,l,mid);
if(opr>mid) calc(x+x+1,mid+1,r);
update(x);
}
int main()
{
freopen("gene.in","r",stdin);
freopen("gene.out","w",stdout);
scanf("%d%d",&n,&m);
scanf("%s",a+1);
fo(i,1,m)
{
read(c[i][0]),read(c[i][1]);
L[2*c[i][0]-1].push_back(i);
R[2*c[i][1]+1].push_back(i);
}
Manacher();
fd(i,n,1)
{
S[i-f[i]].push_back(i);
for(;!S[i].empty();S[i].pop_back())
{
opl=opr=S[i].back(),opx=3;
calc(1,1,n);
}
opl=i+1,opr=n,opx=1,calc(1,1,n);
for(;!L[i].empty();L[i].pop_back())
{
int k=L[i].back();
opl=i,opr=c[k][0]+c[k][1],opx=2;
sum=0,calc(1,1,n),ans[k]+=sum;
}
}
memset(s,0,sizeof(s));
memset(d,0,sizeof(d));
memset(p,0,sizeof(p));
fo(i,1,n)
{
S[i+f[i]].push_back(i);
for(;!S[i].empty();S[i].pop_back())
{
opl=opr=S[i].back(),opx=3;
calc(1,1,n);
}
opl=1,opr=i-1,opx=1,calc(1,1,n);
for(;!R[i].empty();R[i].pop_back())
{
int k=R[i].back();
opl=c[k][0]+c[k][1]+1,opr=i,opx=2;
sum=0,calc(1,1,n),ans[k]+=sum;
}
}
fo(i,1,m) write((ans[i]+c[i][1]-c[i][0]+1)/2),putchar('\n');
}