Luogu6292 区间本质不同子串个数
Luogu6292 区间本质不同子串个数
\(SAM+LCT+Segment\_Tree\)
如果把一个子串看成一个元素,我们首先需要完成的是如何快速判断一个区间内的元素个数。
如果元素是数字,那么我们可以离线操作,枚举右端点\(r\),同时不断更新每个元素出现的位置,也就是每当一个元素出现,我们把它在原来位置的贡献减去,在新的位置加上,很容易证明这种方法能够做到不重不漏。
但是在字符串的子串中,元素个数可以达到\(n^2\)级别,暴力枚举显然不可行。
我们建立\(SAM\),然后观察询问的答案在\(SAM\)上上以怎样的形式呈现的。
我们需要维护每一个子串的最新出现的左端点,那么我们考虑,当增加一个字符\(S_r\)时,有多少个串会受到影响。
首先找到字符串\(r\)位置在\(SAM\)上的位置\(p\),显然这个位置包括了\(S[1\cdots r]\)这个最大的子串,那么同样以\(r\)为末位置的后缀必然是\(parent\)树上从\(p\)到根的路径,这条路径上所有点的集合中包含的串都是需要更新的(对于\(p\)节点,只有长度\(\le r\)的一部分被包含)。
我们需要简化运算,可以发现,对于一个节点所包含的集合,由于它们是等价的,因此它们的末位置相同,而长度连续,因此我们可以在线段树上区间修改。
但是这样仍然需要暴力跳\(parent\)树,最坏时间复杂度\(O(nq \log n)\)。
可以发现,我们进行的操作是很特殊的,也就是把一个节点\(p\)到根\(1\)的路径赋值为同一个,树上链赋新值,总共会产生的颜色段数为\(n \log n\),如何证明,显然这个操作就是\(LCT\)的\(access\)操作,对于链赋新值,可以看一下Luogu3703 [SDOI2017]树点涂色,这题的\(LCT\)解法与本题十分类似。
也就是说,我们总共需要修改的异色颜色段数总共为\(n \log n\)个,而我们却花了\(n^2\)的时间去求解它。
我们考虑,同色颜色段代表着什么,这意味着它们的左端点是相同的,而同色颜色段以树上链的形式分布,这条链代表的子串是连续的,因此我们可以把整条链抽出来进行区间减操作。
因此我们维护\(LCT\),在同一个\(splay\)中节点正代表一个同色颜色段。
同时\(access\)有一个性质,除了第一个节点以外,其他访问的节点都是从询问节点到根的路径在\(splay\)中最深的节点(比它更深的一定不在访问的链上),因此覆盖的区间为\([r_x-len_x+1,r_x-len_{fa_x}]\)(\(fa_x\)为\(LCT\)上的父亲),然后特判\(1\)号节点即可。
自此,我们已经在\(O(n \log^2 n)\)的时间内完成了这道看似极其困难的题目。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#define ll long long
#define rint register int
#define il inline
#define pr pair<int,int>
#define mp make_pair
#define IT vector< pr > :: iterator
#define N 100005
#define M 200005
using namespace std;
int n,m,l,r;
int cnt=1,lst=1,t[N << 1][26],pre[N << 1],len[N << 1];
char s[N];
vector< pr >e[N];
ll ans[M];
il int read()
{
int s=0;
char c=getchar();
while (c<'0' || c>'9')
c=getchar();
while ('0'<=c && c<='9')
s=(s << 3)+(s << 1)+(c^48),c=getchar();
return s;
}
void write(ll x)
{
if (x>9)
write(x/10);
putchar(x%10+48);
}
il void ins(int c)
{
int p=lst,q,np=++cnt;
len[np]=len[p]+1;
lst=np;
for (;p && !t[p][c];p=pre[p])
t[p][c]=np;
if (!p)
pre[np]=1; else
{
int q=t[p][c];
if (len[p]+1==len[q])
pre[np]=q; else
{
int g=++cnt;
memcpy(t[g],t[q],sizeof(t[q]));
len[g]=len[p]+1,pre[g]=pre[q];
for (;p && t[p][c]==q;p=pre[p])
t[p][c]=g;
pre[np]=pre[q]=g;
}
}
}
struct node
{
ll s;
int l,r,tag;
}tr[N << 2];
il void push_tag(int p,int tag)
{
if (!p)
return;
tr[p].s+=(ll)tag*(tr[p].r-tr[p].l+1);
tr[p].tag+=tag;
}
il void push_down(int p)
{
if (tr[p].tag)
{
push_tag(p << 1,tr[p].tag);
push_tag(p << 1 | 1,tr[p].tag);
tr[p].tag=0;
}
}
il void update(int p)
{
tr[p].s=tr[p << 1].s+tr[p << 1 | 1].s;
}
void build(int p,int l,int r)
{
tr[p].l=l,tr[p].r=r;
if (l==r)
return;
int mid=(l+r) >> 1;
build(p << 1,l,mid);
build(p << 1 | 1,mid+1,r);
}
void modify(int p,int l,int r,int x,int y,int z)
{
if (l==x && r==y)
{
push_tag(p,z);
return;
}
push_down(p);
int mid=(l+r) >> 1;
if (y<=mid)
modify(p << 1,l,mid,x,y,z); else
if (x>mid)
modify(p << 1 | 1,mid+1,r,x,y,z); else
{
modify(p << 1,l,mid,x,mid,z);
modify(p << 1 | 1,mid+1,r,mid+1,y,z);
}
update(p);
}
ll calc(int p,int l,int r,int x,int y)
{
if (l==x && r==y)
return tr[p].s;
push_down(p);
int mid=(l+r) >> 1;
if (y<=mid)
return calc(p << 1,l,mid,x,y); else
if (x>mid)
return calc(p << 1 | 1,mid+1,r,x,y); else
return calc(p << 1,l,mid,x,mid)+calc(p << 1 | 1,mid+1,r,mid+1,y);
}
#define ls(x) a[x].ch[0]
#define rs(x) a[x].ch[1]
#define fa(x) a[x].f
#define ts(x) a[x].stg
#define tg(x) a[x].tag
struct LCT
{
int ch[2],f;
int stg=0,tag=0;
}a[N << 1];
int q[N << 1];
il void push_g(int x,int z)
{
if (!x)
return;
ts(x)=tg(x)=z;
}
il void push_d(int x)
{
if (tg(x))
{
push_g(ls(x),tg(x));
push_g(rs(x),tg(x));
tg(x)=0;
}
}
il bool isrt(int x)
{
return ls(fa(x))!=x && rs(fa(x))!=x;
}
il int id(int x)
{
return ls(fa(x))==x?0:1;
}
il void connect(int x,int F,int son)
{
fa(x)=F;
a[F].ch[son]=x;
}
il void rot(int x)
{
int y=fa(x),r=fa(y);
int yson=id(x),rson=id(y);
if (isrt(y))
fa(x)=r; else
connect(x,r,rson);
connect(a[x].ch[yson^1],y,yson);
connect(y,x,yson^1);
}
il void splay(int x)
{
int g=x,k=0;
q[++k]=g;
while (!isrt(g))
g=fa(g),q[++k]=g;
while (k)
push_d(q[k--]);
while (!isrt(x))
{
int y=fa(x);
if (isrt(y))
rot(x); else
if (id(x)==id(y))
rot(y),rot(x); else
rot(x),rot(x);
}
}
il void access(int x,int r)
{
int y;
for (y=0;x;y=x,x=fa(x))
{
splay(x);
if (x!=1 && ts(x))
modify(1,1,n,ts(x)-len[x]+1,ts(x)-len[fa(x)],-1);
rs(x)=y;
}
push_g(y,r);
modify(1,1,n,1,r,1);
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for (rint i=1;i<=n;++i)
ins(s[i]-'a');
m=read();
for (rint i=1;i<=m;++i)
l=read(),r=read(),e[r].push_back(mp(l,i));
build(1,1,n);
for (rint i=2;i<=cnt;++i)
fa(i)=pre[i];
int st=1;
for (rint i=1;i<=n;++i)
{
st=t[st][s[i]-'a'];
access(st,i);
for (IT it=e[i].begin();it!=e[i].end();++it)
ans[it->second]=calc(1,1,n,it->first,i);
}
for (rint i=1;i<=m;++i)
write(ans[i]),putchar('\n');
return 0;
}