区间子串个数

被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;
}
posted @ 2019-11-06 10:38  源曲明  阅读(449)  评论(0编辑  收藏  举报