【XSY2715】回文串 树链剖分 回文自动机

时间:2023-03-09 13:29:18
【XSY2715】回文串 树链剖分 回文自动机

题目描述

  有一个字符串\(s\),长度为\(n\)。有\(m\)个操作:

  • \(addl ~c\):在\(s\)左边加上一个字符\(c\)
  • \(addr~c\):在\(s\)右边加上一个字符
  • \(transl~l_1~r_1~l_2~r_2\):有两个\(s\)的子串\(s_1=s[l_1\ldots r_1],s_2=s[l_2\ldots r_2]\)。你要把\(s_1\)变成\(s_2\)。每次允许在左边加一个字符或删一个字符。要求操作次数最少。定义一个字符串是好的当且仅当这个字符串是回文串且在操作过程中出现。记

\[ans=\sum_{i=1}^n\sum_{j=i}^n[s[i\ldots j]~is~good]\times (j-i+1)
\]

  求\(ans\)

  • \(transr~l_1~r_1~l_2~r_2\):和上一个操作类似,但是只允许在右边加入或删除字符

\[<Empty \space Math \space Block>
\]

  \(n,m\leq 100000\)

题解

  毒瘤题。

  先把所有操作保存下来,求出最终的字符串。

  观察到操作过程中每个字符串都只会出现一次,而且左边的一部分相同。

  可以用回文自动机完成操作。

  先把回文自动机建出来,把fail树剖分。

  处理出每个前缀/后缀的最长回文子串。

  询问时先在fail树上面跳得到在范围内的最长回文子串。

  那么答案就是这条链上所有回文串的出现次数\(\times\)长度的和。

  如果lca不是这两个串的lcp,那么就要减掉lca的值。

  插入时找到在当前范围内的最长回文前缀/后缀,把这个点到根上所有字符串的出现次数\(+1\)

  时间复杂度:\(O((n+m)\log^2 n)\)

  其实开始给你的那个字符串可以在\(O(n)\)内预处理完成的,总的复杂度就是\(O(n+m\log^2 n)\),不过我懒得写了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
namespace pam
{
int s[200010];
int next[200010][30];
int fail[200010];
int len[200010];
int last;
int n;
int p;
void init(int x)
{
int i;
for(i=1;i<=26;i++)
next[x][i]=2;
}
void init()
{
init(1);
init(2);
len[1]=-1;
fail[1]=2;
len[2]=0;
fail[2]=1;
s[0]=0;
p=2;
n=0;
last=2;
}
void insert(int x)
{
s[++n]=x;
while(s[n-len[last]-1]!=s[n])
last=fail[last];
int cur=last;
if(next[cur][x]==2)
{
int now=++p;
init(now);
len[now]=len[cur]+2;
last=fail[last];
while(s[n-len[last]-1]!=s[n])
last=fail[last];
fail[now]=next[last][x];
next[cur][x]=now;
}
last=next[cur][x];
}
void rebuild()
{
last=2;
n=0;
}
void insert2(int x)
{
s[++n]=x;
while(s[n-len[last]-1]!=s[n])
last=fail[last];
int cur=last;
last=next[cur][x];
}
}
namespace sgt
{
struct node
{
int l,r,ls,rs;
ll v;
ll s;
int t;
};
node a[400010];
int n;
int rt;
void build(int &p,int l,int r,int *v)
{
p=++n;
a[p].l=l;
a[p].r=r;
if(l==r)
{
a[p].v=v[l];
return;
}
int mid=(l+r)>>1;
build(a[p].ls,l,mid,v);
build(a[p].rs,mid+1,r,v);
a[p].v=a[a[p].ls].v+a[a[p].rs].v;
}
void add(int p,int t)
{
a[p].t+=t;
a[p].s+=a[p].v*t;
}
void push(int p)
{
if(a[p].t)
{
add(a[p].ls,a[p].t);
add(a[p].rs,a[p].t);
a[p].t=0;
}
}
void add(int p,int l,int r,int v)
{
if(l<=a[p].l&&r>=a[p].r)
{
add(p,v);
return;
}
push(p);
int mid=(a[p].l+a[p].r)>>1;
if(l<=mid)
add(a[p].ls,l,r,v);
if(r>mid)
add(a[p].rs,l,r,v);
a[p].s=a[a[p].ls].s+a[a[p].rs].s;
}
ll query(int p,int l,int r)
{
if(l<=a[p].l&&r>=a[p].r)
return a[p].s;
int mid=(a[p].l+a[p].r)>>1;
push(p);
ll s=0;
if(l<=mid)
s+=query(a[p].ls,l,r);
if(r>mid)
s+=query(a[p].rs,l,r);
return s;
}
}
using sgt::rt;
namespace tree
{
struct graph
{
int h[200010];
int v[200010];
int t[200010];
int n;
void add(int x,int y)
{
n++;
v[n]=y;
t[n]=h[x];
h[x]=n;
}
};
graph g;
void addedge(int x,int y)
{
g.add(x,y);
}
int f[200010];
int d[200010];
int w[200010];
int t[200010];
int s[200010];
int ms[200010];
int c[200010];
int v[200010];
int e[200010];
int ti;
void dfs1(int x,int fa,int dep)
{
f[x]=fa;
d[x]=dep;
s[x]=1;
int mx=0;
int i;
for(i=g.h[x];i;i=g.t[i])
{
dfs1(g.v[i],x,dep+1);
s[x]+=s[g.v[i]];
if(s[g.v[i]]>mx)
{
mx=s[g.v[i]];
ms[x]=g.v[i];
}
}
}
void dfs2(int x,int top)
{
t[x]=top;
w[x]=++ti;
c[ti]=x;
e[ti]=v[x];
if(!ms[x])
return;
dfs2(ms[x],top);
int i;
for(i=g.h[x];i;i=g.t[i])
if(g.v[i]!=ms[x])
dfs2(g.v[i],g.v[i]);
}
int find(int x,int y)
{
while(v[t[x]]>y)
x=f[t[x]];
return c[upper_bound(e+w[t[x]],e+w[x]+1,y)-e-1];
}
void add(int x,int v)
{
while(x)
{
sgt::add(rt,w[t[x]],w[x],v);
x=f[t[x]];
}
}
ll query(int x,int y)
{
ll s=0;
while(t[x]!=t[y])
if(d[t[x]]>d[t[y]])
{
s+=sgt::query(rt,w[t[x]],w[x]);
x=f[t[x]];
}
else
{
s+=sgt::query(rt,w[t[y]],w[y]);
y=f[t[y]];
}
if(w[x]<w[y])
s+=sgt::query(rt,w[x],w[y]);
else
s+=sgt::query(rt,w[y],w[x]);
return s;
}
int getlca(int x,int y)
{
while(t[x]!=t[y])
if(d[t[x]]>d[t[y]])
x=f[t[x]];
else
y=f[t[y]];
return w[x]<w[y]?x:y;
}
}
int op[100010],ql1[100010],qr1[100010],ql2[100010],qr2[100010];
int n,m;
int s[200010],s1[100010],s2[100010],s3[100010];
int pre[200010],suf[200010];
int nl,nr;
void addr()
{
nr++;
int x=tree::find(pre[nr],nr-nl+1);
tree::add(x,1);
}
void addl()
{
nl--;
int x=tree::find(suf[nl],nr-nl+1);
tree::add(x,1);
}
void queryl(int l1,int r1,int l2,int r2)
{
int x=tree::find(pre[r1],r1-l1+1);
int y=tree::find(pre[r2],r2-l2+1);
int lca=tree::getlca(x,y);
ll ans=tree::query(x,y);
int len=tree::v[lca];
if(r1-l1+1!=len&&r2-l2+1!=len&&s[r1-len]==s[r2-len])
ans-=sgt::query(rt,tree::w[lca],tree::w[lca]);
printf("%lld\n",ans);
}
void queryr(int l1,int r1,int l2,int r2)
{
int x=tree::find(suf[l1],r1-l1+1);
int y=tree::find(suf[l2],r2-l2+1);
int lca=tree::getlca(x,y);
ll ans=tree::query(x,y);
int len=tree::v[lca];
if(r1-l1+1!=len&&r2-l2+1!=len&&s[l1+len]==s[l2+len])
ans-=sgt::query(rt,tree::w[lca],tree::w[lca]);
printf("%lld\n",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
char str[10];
scanf("%d%d",&n,&m);
int n1=0,n2=0;
int i;
for(i=1;i<=n;i++)
{
scanf("%d",&s3[i]);
s3[i]++;
}
for(i=1;i<=m;i++)
{
scanf("%s",str);
if(str[0]=='a')
{
scanf("%d",&ql1[i]);
ql1[i]++;
if(str[3]=='l')
{
op[i]=1;
s1[++n1]=ql1[i];
}
else
{
op[i]=2;
s2[++n2]=ql1[i];
}
}
else
{
scanf("%d%d%d%d",&ql1[i],&qr1[i],&ql2[i],&qr2[i]);
if(str[5]=='l')
op[i]=3;
else
op[i]=4;
}
}
int num=0;
for(i=m;i>=1;i--)
{
if(op[i]==1)
num++;
else if(op[i]>=2)
{
ql1[i]+=num;
qr1[i]+=num;
ql2[i]+=num;
qr2[i]+=num;
}
}
int len=0;
for(i=n1;i>=1;i--)
s[++len]=s1[i];
for(i=1;i<=n;i++)
s[++len]=s3[i];
for(i=1;i<=n2;i++)
s[++len]=s2[i];
pam::init();
for(i=1;i<=len;i++)
{
pam::insert(s[i]);
pre[i]=pam::last;
}
pam::rebuild();
for(i=len;i>=1;i--)
{
pam::insert2(s[i]);
suf[i]=pam::last;
}
for(i=2;i<=pam::p;i++)
{
tree::addedge(pam::fail[i],i);
tree::v[i]=pam::len[i];
}
tree::dfs1(1,0,1);
tree::dfs2(1,1);
sgt::build(rt,1,pam::p,tree::e);
nl=n1+1;
nr=n1;
for(i=1;i<=n;i++)
addr();
for(i=1;i<=m;i++)
if(op[i]==1)
addl();
else if(op[i]==2)
addr();
else if(op[i]==3)
queryl(ql1[i],qr1[i],ql2[i],qr2[i]);
else
queryr(ql1[i],qr1[i],ql2[i],qr2[i]);
return 0;
}