区间子串个数
被jz姐姐欺负了之后去学了一下这个东西
参考资料:陈江伦18年集训队论文《《后缀树结点数》命题报告及一类区间问题的优化》
给定一个串\(s\),\(q\)次询问\(s[l,r]\)的本质不同的子串个数
sol1
考虑离线,把所有的询问都给存到右端点上,建好\(SAM\)
先考虑暴力,每一次右端点从\(r-1\)移到\(r\)时,记\(id[r]\)表示在\(SAM\)上对应的节点,\(end[p]\)为\(SAM\)上的节点\(p\)当前最后一次出现的位置。对于每一个子串,我们只需要关心它最后一次出现的位置就行了。我们在线段树上每个点记录位于该点的合法的子串的左端点个数,那么每一次只要区间查询就行了
对于\(id[r]\)到根节点上的每个节点\(p\),它的长度是\(l[fa[p]]+1\)到\(l[p]\),如果\(end[p]\)不为\(0\),那么我们把\([end[p]-l[p]+1,end[p]-l[fa[p]]]\)区间减一,最后再把\([1,r]\)区间加一,然后就可以直接区间查询了
我们发现一件事,如果某一次\(fa[p]\)的更新来自\(p\)的子树,下一次\(fa[p]\)更新的时候,更新的节点依然来自\(p\)的子树,那么这一次我们就可以把\(fa[p]\)和\(p\)放在一起考虑了
然后我们发现这个东西就是\(LCT\)的一次\(access\)操作,那么用\(LCT\)维护就行了,复杂度\(O(n\log^2 n)\)
sol2
然而\(LCT\)对于曲明这种从不背板选手不是很友好,曲明完全没有自信可以在考场上码对,于是自己\(yy\)了一下laofu论文里说的另一种树上启发式合并的方法
我们发现,对于一个点,它儿子链的虚实切换次数是和轻儿子个数是同一个级别的,所以所有点的儿子链切换次数是\(O(n\log n)\)(这也可以用来证明\(LCT\)的\(access\)次数)
我们考虑用dsu on tree来优化,对于每个点\(u\),把它的子树里所有的\(endpos\)都取出来,那么可以认为,\(u\)的实链会一直指向重儿子,而轻儿子\(access\)的时候会产生两个断点
那么我们遍历轻儿子里所有的\(endpos\),对于每个\(endpos\),记为\(p\),找到\(u\)的子树中它的前驱\(Pre\),那么在\(p\)进行\(access\)的时候,\(u\)这个节点处在以\(Pre\)为端点的实链上,那么我们只要把这个记录下来,就可以在扫到\(p\)的时候再去更改。同理如果\(p\)的后继是\(suf\),那么\(suf\)在\(access\)的时候需要更改此时处于\(p\)为端点的实链上的\(u\)。只要把所有的这样的操作都记录下来就行了
这样的话,当我们扫到某个\(p\),我们把它的所有操作按需要更改的节点的长度排序,同时根据这个需要更改的节点处于哪个端点的实链上改一下就行了
复杂度\(O(n\log^2 n)\),跑得比上面那个慢一丢丢(可能是我写萎了,如果又发现哪里可以优化的可以在下面嘲讽曲明)
但是对于从来码不对LCT的曲明来说要好写不少
代码1
//2019.11.5 by ljz
//#pragma GCC optimize(2)
//email 573902690@qq.com
//if you find any bug in my code
//please tell me
#include<bits/stdc++.h>
//#include<ext/pb_ds/tree_policy.hpp>
//#include<ext/pb_ds/assoc_container.hpp>
using namespace std;
//using namespace __gnu_pbds;
//using namespace __gnu_cxx;
#define res register int
#define LL long long
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f
#define unl __int128
#define eps 1e-9
#define RG register
#define db double
//#define pc(x) __builtin_popcount(x)
#define ctz(x) __builtin_ctz(x)
#define pc(x) __builtin_popcountll(x)
typedef pair<int,int> Pair;
typedef pair<int,int> pi;
#define mp make_pair
#define fi first
#define se second
#define ull unsigned LL
#define lowbit(x) (x&-x)
#define gc getchar
#include<bits/stdc++.h>
#define pb emplace_back
#define fp(i,a,b) for(RG int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(RG int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
//template <class T>using Tree=tree<T,null_type,less<T>,rb_tree_tag,tree_order_statistics_node_update>;
//inline char gc() {
// static char buf[100000],*p1,*p2;
// return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
//}
//inline int read() {
// res s=0,ch=gc();
// while(ch<'0'||ch>'9')ch=gc();
// while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=gc();
// return s;
//}
//char sr[1<<21],z[20];
//int C=-1,Z=0;
//inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
//inline void print(res x){
// if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
// while(z[++Z]=x%10+48,x/=10);
// while(sr[++C]=z[Z],--Z);sr[++C]='\n';
//}
inline int read() {
res s=0,ch=gc(),w=1;
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=gc();}
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=gc();
return s*w;
}
inline LL Read() {
RG LL s=0;
res ch=gc();
while(ch<'0'||ch>'9')ch=gc();
while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=gc();
return s;
}
//inline LL Read() {
// RG LL s=0;
// res ch=gc(),w=1;
// while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=gc();}
// while(ch>='0'&&ch<='9')s=s*10+ch-'0',ch=gc();
// return s*w;
//}
//inline void write(RG unl x){
// if(x>10)write(x/10);
// putchar(int(x%10)+'0');
//}
inline void swap(res &x,res &y) {
x^=y^=x^=y;
}
//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
//clock_t start=clock();
//inline void ck(){
// if(1.0*(clock()-start)/CLOCKS_PER_SEC>0.1)exit(0);
//}
const int kcz=1000000007;
inline void add(res &x,const res &y){
x+=y,x>=kcz?x-=kcz:(x<0?x+=kcz:1);
}
inline int Add(const res &x,const res &y){
return x+y>=kcz?x+y-kcz:(x+y<0?x+y+kcz:x+y);
}
inline int mul(const res &x,const res &y){
return int(1LL*x*y%kcz);
}
inline int mul(const res &x,const res &y,const res &d){
return int(1LL*x*y/d%kcz);
}
inline int sqr(const res &x){
return int(1LL*x*x%kcz);
}
inline int qpow(res x,res y){
res ret=1;
while(y){
if(y&1)ret=mul(ret,x);
x=mul(x,x),y>>=1;
}
return ret;
}
inline LL Qpow(RG LL x,res y){
RG LL ret=1;
while(y){
if(y&1)ret*=x;
x*=x,y>>=1;
}
return ret;
}
const int N=5e5+10;
int n;char s[N];
int pos[N];
struct SAM{
struct Sam{
int vis[26],par,len,pos;
}sam[N<<1];
int las,rt,cnt;
SAM() {las=rt=cnt=1;}
inline void extend(const res &x,const res &id){
res p=las,np=++cnt;
las=np,sam[np].len=sam[p].len+1,pos[id]=las;
for(;p&&!sam[p].vis[x];p=sam[p].par)sam[p].vis[x]=np;
if(!p)sam[np].par=rt;
else {
res q=sam[p].vis[x];
if(sam[q].len==sam[p].len+1)sam[np].par=q;
else {
res nq=++cnt;
memcpy(sam[nq].vis,sam[q].vis,sizeof(sam[nq].vis));
sam[nq].len=sam[p].len+1;
sam[nq].par=sam[q].par;
sam[q].par=sam[np].par=nq;
for(;p&&sam[p].vis[x]==q;p=sam[p].par)sam[p].vis[x]=nq;
}
}
}
}A;
typedef pair<int,int> Pair;
#define mp make_pair
#define fi first
#define se second
int q;
LL ans[N];
vector<Pair> vec[N];
struct Seg{
LL sum[N<<2];
int add[N<<2],len[N<<2];
inline void pushup(const res &rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
inline void change(const res &rt,const res &val){
sum[rt]+=1LL*val*len[rt],add[rt]+=val;
}
inline void pushdown(const res &rt){
if(!add[rt])return;
change(rt<<1,add[rt]),change(rt<<1|1,add[rt]),add[rt]=0;
}
void build(const res &rt,const res &l,const res &r){
len[rt]=r-l+1;
if(l==r)return;
res mid=(l+r)>>1;
build(rt<<1,l,mid),build(rt<<1|1,mid+1,r);
}
void modify(const res &rt,const res &l,const res &r,const res &L,const res &R,const res &val){
if(L>R)return;
if(L<=l&&r<=R){change(rt,val);return;}
pushdown(rt);
res mid=(l+r)>>1;
if(L<=mid)modify(rt<<1,l,mid,L,R,val);
if(R>mid)modify(rt<<1|1,mid+1,r,L,R,val);
pushup(rt);
}
LL query(const res &rt,const res &l,const res &r,const res &L,const res &R){
if(L>R)return 0;
if(L<=l&&r<=R)return sum[rt];
pushdown(rt);
res mid=(l+r)>>1;
LL ret=0;
if(L<=mid)ret+=query(rt<<1,l,mid,L,R);
if(R>mid)ret+=query(rt<<1|1,mid+1,r,L,R);
return ret;
}
}B;
inline int _max(RG int x,RG int y){return x>y?x:y;}
inline int _min(RG int x,RG int y){return x<y?x:y;}
inline void _swap(RG int &x,RG int &y){RG int t=x;x=y,y=t;}
struct LCT{
struct Lct{
int son[2],sz,fa,mn,mx,mxlen,mnlen,col;
bool rev;
}tr[N];
inline void pushup(const res &x){
if(!x)return;
res ls=tr[x].son[0],rs=tr[x].son[1];
tr[x].sz=tr[ls].sz+tr[rs].sz+1;
tr[x].mx=_max(_max(tr[ls].mx,tr[rs].mx),tr[x].mxlen);
tr[x].mn=_min(_min(tr[ls].mn,tr[rs].mn),tr[x].mnlen);
}
inline void reversed(const res &x){
if(!x)return;
res &ls=tr[x].son[0],&rs=tr[x].son[1];
_swap(ls,rs),tr[x].rev^=1;
}
inline void pushdown(const res &x){
if(!x||!tr[x].rev)return;
res ls=tr[x].son[0],rs=tr[x].son[1];
reversed(ls),reversed(rs),tr[x].rev=0;
}
inline bool nroot(const res &x) {
return tr[tr[x].fa].son[0]==x||tr[tr[x].fa].son[1]==x;
}
inline void rotate(const res &x) {
res y=tr[x].fa,z=tr[y].fa,k=tr[y].son[1]==x,w=tr[x].son[k^1];
if(nroot(y))tr[z].son[tr[z].son[1]==y]=x;
tr[x].son[k^1]=y,tr[y].son[k]=w;
if(w)tr[w].fa=y;
tr[y].fa=x,tr[x].fa=z;
pushup(y);
}
int st[N];
inline void splay(const res &x) {
res i=x,s=0;
st[++s]=x;
while(nroot(i))st[++s]=i=tr[i].fa;
res t=tr[st[s]].col;
while(s)pushdown(st[s--]);
while(nroot(x)) {
res y=tr[x].fa,z=tr[y].fa;
if(nroot(y))rotate((tr[y].son[0]==x)^(tr[z].son[0]==y)?x:y);
rotate(x);
}
pushup(x);
tr[x].col=t;
}
inline void access(res x,const res &val){
res y;
for(y=0;x;x=tr[y=x].fa){
splay(x);
res Mn=0,Mx=A.sam[x].len;
if(tr[x].son[0])Mn=_max(tr[tr[x].son[0]].mn,1);
else Mn=A.sam[A.sam[x].par].len+1;
if(tr[x].col)B.modify(1,1,n,tr[x].col-Mx+1,tr[x].col-Mn+1,-1);
tr[tr[x].son[1]].col=tr[x].col,tr[x].son[1]=y,pushup(x);
}
tr[y].col=val,B.modify(1,1,n,val-tr[y].mx+1,val,1);
}
inline void calc(){
tr[0].mn=inf;
for(res i=1;i<=A.cnt;i++){
tr[i].sz=1,tr[i].fa=A.sam[i].par;
if(i==1)tr[i].mn=tr[i].mnlen=0;
else tr[i].mn=tr[i].mnlen=A.sam[A.sam[i].par].len+1;
tr[i].mx=tr[i].mxlen=A.sam[i].len;
}
for(res i=1;i<=n;i++){
access(pos[i],i);
for(res j=0,sz=vec[i].size();j<sz;j++)ans[vec[i][j].se]=B.query(1,1,n,vec[i][j].fi,i);
}
}
}T;
namespace MAIN{
void MAIN(){
scanf("%s",s+1),n=strlen(s+1);
fp(i,1,n)A.extend(s[i]-'a',i);
B.build(1,1,n);
scanf("%d",&q);
for(RG int i=1,l,r;i<=q;++i){
scanf("%d%d",&l,&r);
vec[r].pb(pi(l,i));
}
T.calc();
fp(i,1,q)printf("%lld\n",ans[i]);
}
}
int main(){
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
MAIN::MAIN();
return 0;
}
代码2
//quming
#include<bits/stdc++.h>
#define R register
#define fi first
#define se second
#define pb emplace_back
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int i=hc[u],v=E[i].v;i;i=E[i].nx,v=E[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
typedef pair<int,int> pi;
const int N=5e5+5,M=N*10;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int n,m;char str[N];
namespace BIT{
struct node;typedef node* ptr;
struct node{ptr lc,rc;ll s;int t;}e[N<<2],*rt,*pp=e;
void build(ptr &p,int l,int r){
p=++pp;if(l==r)return;
int mid=(l+r)>>1;
build(p->lc,l,mid),build(p->rc,mid+1,r);
}
void update(ptr p,int l,int r,int ql,int qr,int d){
p->s+=d*(qr-ql+1);if(ql<=l&&qr>=r)return p->t+=d,void();
int mid=(l+r)>>1;
if(qr<=mid)return update(p->lc,l,mid,ql,qr,d);
if(ql>mid)return update(p->rc,mid+1,r,ql,qr,d);
update(p->lc,l,mid,ql,mid,d),update(p->rc,mid+1,r,mid+1,qr,d);
}
ll query(ptr p,int l,int r,int ql,int qr,int d){
if(ql<=l&&qr>=r)return p->s+d*(qr-ql+1);
int mid=(l+r)>>1;d+=p->t;
if(qr<=mid)return query(p->lc,l,mid,ql,qr,d);
if(ql>mid)return query(p->rc,mid+1,r,ql,qr,d);
return query(p->lc,l,mid,ql,mid,d)+query(p->rc,mid+1,r,mid+1,qr,d);
}
inline ll query(R int l,R int r){return query(rt,1,n,l,r,0);}
inline void upd(R int l,R int r,R int d){update(rt,1,n,l,r,d);}
inline void build(){build(rt,1,n);}
}
//namespace BIT{
// //区间修改区间查询树状数组
// ll d[N];int c[N];
// inline void chgd(R int x,R int v){for(;x<=n;x+=x&-x)d[x]+=v;}
// inline void chgc(R int x,R int v){for(;x<=n;x+=x&-x)c[x]+=v;}
// inline void upd(R int l,R int r,R int v){
// chgc(r+1,-v),chgd(r+1,-v*(r+1));
// if(l!=0)chgc(l,v),chgd(l,v*l);
// }
// inline int askc(R int x){R int res=0;for(;x;x-=x&-x)res+=c[x];return res;}
// inline ll askd(R int x){R ll res=0;for(;x;x-=x&-x)res+=d[x];return res;}
// inline ll query(R int l,R int r){
// R ll res=0;
// res+=1ll*(r+1)*askc(r)-askd(r);
// if(l>1)res-=1ll*l*askc(l-1)-askd(l-1);
// return res;
// }
//}
namespace ljz{
int l[N],fa[N],ch[N][26],cnt=1,las=1;
void ins(int c,int p=las){
int np=las=++cnt;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[q]==l[p]+1)fa[np]=q;
else{
int nq=++cnt;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],104);
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
}
struct chg{int v,nx,w;}E[M];int hc[N],tc;
inline void Add(R int u,R int v,R int w){E[++tc]={v,hc[u],w},hc[u]=tc;}
int dfn[N],sz[N],son[N],id[N],pos[N],tim;
set<int>s;typedef set<int>::iterator IT;
void dfs1(int u){
sz[u]=(id[u]!=0),son[u]=0;
go(u){
dfs1(v),sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
void inc(int u){if(id[u])s.insert(id[u]),dfn[id[u]]=tim;go(u)inc(v);}
void get(int u,int sn,int rt){
// printf("%d %d %d\n",u,sn,rt);
if(id[u]){
IT it=s.lower_bound(id[u]),itl=it;
if(it!=s.begin()&&dfn[*--it]!=dfn[id[u]])Add(id[u],rt,*it);
++itl;
if(itl!=s.end()&&dfn[*itl]!=dfn[id[u]])Add(*itl,rt,id[u]);
}
go(u)if(v!=sn)get(v,sn,rt);
}
void dfs2(int u){
// printf("%d\n",u);
go(u)if(v!=son[u])dfs2(v),s.clear();
if(son[u])dfs2(son[u]);
if(id[u])s.insert(id[u]),dfn[id[u]]=++tim;
go(u)if(v!=son[u])++tim,inc(v);
if(u!=1)get(u,son[u],u);
}
vector<pi>qr[N];ll ans[N];pi st[N];int top;
inline bool cmp(const pi &a,const pi &b){return l[a.fi]>l[b.fi];}
void MAIN(){
scanf("%s%d",str+1,&m),n=strlen(str+1);
BIT::build();
fp(i,1,n)ins(str[i]-'a'),id[las]=i;
fp(i,2,cnt)add(fa[i],i);
dfs1(1),dfs2(1);
for(R int i=1,l,r;i<=m;++i)scanf("%d%d",&l,&r),qr[r].pb(pi(l,i));
fp(u,1,n){
BIT::upd(1,u,1);
top=0;
gg(u)st[++top]=pi(E[i].v,E[i].w);
st[top+1]=pi(0,0);
sort(st+1,st+1+top,cmp);
fp(i,1,top)if(l[st[i].fi]>l[st[i+1].fi]){
// printf("%d %d")
BIT::upd(st[i].se-l[st[i].fi]+1,st[i].se-l[st[i+1].fi],-1);
}
for(auto v:qr[u])ans[v.se]=BIT::query(v.fi,u);
}
fp(i,1,m)printf("%lld\n",ans[i]);
}
}
int main(){
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
ljz::MAIN();
return 0;
}