万灵药
题解
首先容易发现,他题目中说的某两个前缀的最长公共后缀不就是我们正串
S
A
M
SAM
SAM上的节点吗?
但我们怎么求它们的最长公共前缀之和的答案呢?
有一种非常蠢的做法是,把我们反串的
S
A
M
SAM
SAM建出来,然后把正串上的所有结点在反串上建出它的虚树,显然,两个正串
S
A
M
SAM
SAM上的节点映射到反串
S
A
M
SAM
SAM节点的
l
c
a
lca
lca的深度就是它们的最长公共前缀的长度。
考虑一下上面的这个东西如何在加入删除的过程中维护答案。
显然,两个点割贡献可以看成这两个点到根的路径上重复的部分,那我们可以每加入一个点就把这个点到根路径上的每个点都加上
1
1
1,而一个点与别的点的贡献和就是它到根的这条路径上所有点的贡献之和。
删掉点的时候也只需要撤销掉它的贡献。
上面这东西显然是可以通过树链剖分来维护的。
这样是
O
(
Q
log
2
n
)
O\left(Q\log^2 n\right)
O(Qlog2n)的。
好像过不了
n
⩽
5
×
1
0
5
n\leqslant 5\times 10^5
n⩽5×105,没事,可以发现我们的所有操作都是在树链上进行的,我们可以用全局平衡二叉树来维护,就成功达到
O
(
n
log
n
)
O\left(n\log n\right)
O(nlogn)了。注意一下,全局平衡二叉树事实上是平衡树的形式,而不是线段树,虽然两者复杂度上是没有区别的。
但笔者这样打了只会被疯狂卡常了。建两棵SAM还用倍增插点,不卡你卡谁
于是,笔者就去问了问神
O
U
Y
E
\rm O\color{red}{UYE}
OUYE,原来最开始完全没必要像我们刚刚说的那样建正反两棵
S
A
M
SAM
SAM,然后再去插虚树。
可以发现,你建出来的虚树任意一条边的长度都不会超过
1
1
1。
映射到原树上,如果点对
(
x
,
y
)
(x,y)
(x,y)的
l
c
a
lca
lca深度为
d
(
d
>
0
)
d(d>0)
d(d>0),那么点对
(
x
−
1
,
y
−
1
)
(x-1,y-1)
(x−1,y−1)的
l
c
a
lca
lca深度肯定是
d
−
1
d-1
d−1,它们都会被加入到虚树里面去,也就是说,我们虚树里面的深度肯定是连续的,而且根本不会新建任何虚点,正串
S
A
M
SAM
SAM中的所有点就可以构成虚树。
待って,我们上面不是虚树里的边不都是相当于在原串的后面多了一个字符吗?就是我们上面说的
(
x
−
1
,
y
−
1
)
→
(
x
,
y
)
(x-1,y-1)\rightarrow (x,y)
(x−1,y−1)→(x,y),这不就是正串
S
A
M
SAM
SAM里边的含义吗?
也就是说,正串
S
A
M
SAM
SAM里
D
A
G
DAG
DAG上刚好让长度加
1
1
1的边必然会出现在虚树里面,我们就可以直接通过正串
S
A
M
SAM
SAM构建出这棵树了。
再像上面说的那样建全局平衡二叉树即可。
时间复杂度 O ( n log n ) O\left(n\log n\right) O(nlogn)。
源码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef unsigned int uint;
#define MAXN 500005
#define MAXM 1000005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
char gc(){static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
#define getchar gc
char obuf[1<<22],*opt=obuf+(1<<22);
void pc(const int&ch){*--opt=ch;}
#define putchar pc
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){putchar('\n');while(x>9){putchar((x%10)|'0');x/=10;}putchar(x|'0');}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,Q,dfn[MAXM],sz[MAXM],ltp[MAXM],idx,ip[MAXN],root[MAXM];
int father[MAXM],pre[MAXM],val[MAXM],wson[MAXM],dn[MAXM];LL ans,answer[MAXN];
char str[MAXN];bool vis[MAXM];
struct ming{int lson,rson,len,mid,lzy,dif;LL sm,sum;};
struct node{int ch[4],len,fa,ed;};
class SegmentTree{
private:
ming tr[MAXM];int tot;
void work(int rt,int aw){
tr[rt].sum+=1ll*aw*tr[rt].dif;
tr[rt].sm+=aw;tr[rt].lzy+=aw;
}
void pushdown(int rt){
if(tr[rt].lzy){
if(tr[rt].lson)work(tr[rt].lson,tr[rt].lzy);
if(tr[rt].rson)work(tr[rt].rson,tr[rt].lzy);
tr[rt].lzy=0;
}
}
void pushup(int rt){tr[rt].sum=tr[tr[rt].lson].sum+tr[tr[rt].rson].sum+tr[rt].sm;}
public:
void build(int &rt,int l,int r){
rt=++tot;int mid,all=(sz[pre[l]]-sz[wson[pre[r]]]+1)/2;
for(mid=l+1;mid<=r;mid++)if(sz[pre[l]]-sz[pre[mid]]>all)break;
mid--;tr[rt].mid=mid;tr[rt].dif=r-l+1;
if(l<mid)build(tr[rt].lson,l,mid-1);
if(r>mid)build(tr[rt].rson,mid+1,r);
}
void insert(int rt,int l,int r,int al,int ar){
if(al>r||ar<l||al>ar)return ;int mid=tr[rt].mid;
if(al<=l&&r<=ar){ans+=tr[rt].sum;work(rt,1);return ;}
if(al<=mid&&mid<=ar)ans+=tr[rt].sm,tr[rt].sm++;
pushdown(rt);
if(al<mid)insert(tr[rt].lson,l,mid-1,al,ar);
if(ar>mid)insert(tr[rt].rson,mid+1,r,al,ar);
pushup(rt);
}
void remove(int rt,int l,int r,int al,int ar){
if(al>r||ar<l||al>ar)return ;int mid=tr[rt].mid;
if(al<=l&&r<=ar){work(rt,-1);ans-=tr[rt].sum;return ;}
if(al<=mid&&mid<=ar)tr[rt].sm--,ans-=tr[rt].sm;
pushdown(rt);
if(al<mid)remove(tr[rt].lson,l,mid-1,al,ar);
if(ar>mid)remove(tr[rt].rson,mid+1,r,al,ar);
pushup(rt);
}
}T;
vector<int>P[MAXM],G[MAXM];
class SuffixAutomaton{
public:
node tr[MAXM];int tot,las,dep[MAXM],f[MAXM][20];
public:
void init(){las=++tot;}
int extend(int x,int y){
int p=las,np=las=++tot;tr[np].len=tr[p].len+1;tr[np].ed=y;
for(;p&&!tr[p].ch[x];p=tr[p].fa)tr[p].ch[x]=np;
if(!p){tr[np].fa=1;return np;}int q=tr[p].ch[x];
if(tr[q].len==tr[p].len+1){tr[np].fa=q;return np;}
int nq=++tot;tr[nq]=tr[q];tr[nq].len=tr[p].len+1;
for(;p&&tr[p].ch[x]==q;p=tr[p].fa)tr[p].ch[x]=nq;
tr[q].fa=tr[np].fa=nq;return np;
}
void dosaka(int u,int fa){
dep[u]=dep[fa]+1;f[u][0]=fa;int siz=G[u].size();
for(int i=1;i<20;i++)f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<siz;i++)dosaka(G[u][i],u);
}
int lca(int a,int b){
if(dep[a]>dep[b])swap(a,b);
for(int i=19;i>=0;i--)if(dep[f[b][i]]>=dep[a])b=f[b][i];
if(a==b)return a;
for(int i=19;i>=0;i--)if(f[a][i]^f[b][i])a=f[a][i],b=f[b][i];
return f[a][0];
}
void sakura(){
for(int i=1;i<=tot;i++)
for(int j=0;j<4;j++)
if(tr[tr[i].ch[j]].len==tr[i].len+1)
P[i].pb(tr[i].ch[j]);
for(int i=2;i<=tot;i++)G[tr[i].fa].pb(i);
dosaka(1,0);
}
}S;
void dosaka1(int u,int fa){
father[u]=fa;sz[u]=1;int siz=P[u].size();
for(int i=0;i<siz;i++){
int v=P[u][i];dosaka1(v,u);sz[u]+=sz[v];
if(sz[v]>sz[wson[u]])wson[u]=v;
}
}
void dosaka2(int u,int tp){
dfn[u]=++idx;dn[tp]=idx;ltp[u]=tp;
pre[idx]=u;int siz=P[u].size();
if(wson[u])dosaka2(wson[u],tp);
for(int i=0;i<siz;i++){
int v=P[u][i];if(v==wson[u])continue;
dosaka2(v,v);
}
if(u==tp)T.build(root[u],dfn[u],dn[u]);
}
int main(){
//freopen("elixir.in","r",stdin);
//freopen("elixir.out","w",stdout);
scanf("%s",str+1);read(Q);n=(int)strlen(str+1);
S.init();for(int i=1;i<=n;i++)ip[i]=S.extend(str[i]-'a',i);S.sakura();
dosaka1(1,0);int siz=P[1].size();
for(int i=0;i<siz;i++)dosaka2(P[1][i],P[1][i]);
for(int i=1;i<=Q;i++){
int x,y;read(x);read(y);int z=S.lca(ip[x],ip[y]),w;
if(z==1){answer[i]=ans;continue;}
if(!vis[z]){vis[z]=1;while(z>1)w=ltp[z],T.insert(root[w],dfn[w],dn[w],dfn[w],dfn[z]),z=father[w];}
else{vis[z]=0;while(z>1)w=ltp[z],T.remove(root[w],dfn[w],dn[w],dfn[w],dfn[z]),z=father[w];}
answer[i]=ans;
}
for(int i=Q;i>0;i--)print(answer[i]);
fwrite(opt,1,obuf+(1<<22)-opt,stdout);
return 0;
}