日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

洛谷 - P6292 区间本质不同子串个数(SAM+LCT+线段树)

發(fā)布時(shí)間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 洛谷 - P6292 区间本质不同子串个数(SAM+LCT+线段树) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

題目鏈接:點(diǎn)擊查看

題目大意:給出一個(gè)長(zhǎng)度為 n 的字符串,再給出 m 次詢問,每次詢問需要回答區(qū)間 [ l , r ] 內(nèi)有多少個(gè)本質(zhì)不同的字符串

題目分析:首先簡(jiǎn)化模型,回顧一下如何求解 “區(qū)間內(nèi)有多少個(gè)不同的數(shù)” :SPOJ - DQUERY

求解上面那道題目的方法可謂是五花八門,我們采用其中一種離線的方式:先將所有的詢問儲(chǔ)存下來,按照右端點(diǎn) r 排序后,每次對(duì)于重復(fù)出現(xiàn)的數(shù)字,用線段樹維護(hù)其最后一次出現(xiàn)的位置,那么答案就是 [ l , r ] 中元素的個(gè)數(shù)了

對(duì)于這個(gè)題目而言,將每個(gè)本質(zhì)不同的字符串視為一個(gè)連續(xù)的區(qū)間 [ l , r?],我們只需要維護(hù)左端點(diǎn)最后一次出現(xiàn)的位置即可

因?yàn)樾枰烂總€(gè)子串最后一次出現(xiàn)的位置,所以我們選擇對(duì)字符串構(gòu)造后綴自動(dòng)機(jī),對(duì)于某個(gè)右端點(diǎn) r 來說,對(duì)應(yīng)到 SAM 上,從最長(zhǎng)的那個(gè)前綴 [ 1 , r ] 對(duì)應(yīng)的節(jié)點(diǎn)開始,沿著 parent 樹到根節(jié)點(diǎn)的這條路徑上,都是以 r 為右端點(diǎn)的子串,設(shè) pre[ i ] 為節(jié)點(diǎn) i 上一次出現(xiàn)時(shí)的右端點(diǎn)位置,我們只需要暴跳 father,每次將之前出現(xiàn)過的位置,在線段樹上區(qū)間更新成 -1 (因?yàn)閷?duì)于每個(gè)節(jié)點(diǎn)來說,代表的都是一段連續(xù)的后綴,所以可以用到線段樹的區(qū)間更新),最后再將 [ 1 , r ] , [ 2 , r ] ... [ r , r ] 這 r 個(gè)后綴所代表的子串的左端點(diǎn),即 [ 1 , r ] 在線段樹上 +1 就好了

不過問題是,這樣暴跳 father 的時(shí)間復(fù)雜度是不正確的,直接這樣實(shí)現(xiàn)在本題中只能得到 80 分,有一個(gè)測(cè)試點(diǎn)會(huì)超時(shí)

考慮優(yōu)化,因?yàn)槊看芜x擇一條鏈,自下而上去更新,其本質(zhì)就是:

  • 令這條鏈每個(gè)節(jié)點(diǎn)相應(yīng)的位置在線段樹上區(qū)間更新
  • 令每個(gè)節(jié)點(diǎn)都被端點(diǎn) r 覆蓋(也就是將 pre 都標(biāo)記為 r)
  • 這個(gè)過程其實(shí)就是 LCT 中的 access 函數(shù)的操作,這樣每次更新至多需要更新?logn 個(gè) splay,套上線段樹就是 log^2n,均攤下來就是 nlog^2n 了

    因?yàn)樵?SAM 自底向上的一條路徑上,其中任意連續(xù)的一段,所能表示的后綴也是連續(xù)的,所以在 LCT 的 splay 上只需要維護(hù)一下相應(yīng)重鏈上最短的子串長(zhǎng)度就好了

    因?yàn)樵诒绢}中用不到 LCT 的 link、cut 等操作,所以將相應(yīng)的一些基本函數(shù)都刪掉了,只保留了與 access 函數(shù)有關(guān)的一些基本函數(shù)

    總時(shí)間復(fù)雜度是 O( nlog^2n + mlogn )

    代碼:
    ?

    //#pragma GCC optimize(2) //#pragma GCC optimize("Ofast","inline","-ffast-math") //#pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e6+100;char s[N];vector<pair<int,int>>q[N];LL ans[N];namespace Seg {struct Node{int l,r,len;LL sum,lazy;}tree[N<<2];void pushup(int k){tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;}void pushdown(int k){if(tree[k].lazy){LL lz=tree[k].lazy;tree[k].lazy=0;tree[k<<1].sum+=tree[k<<1].len*lz;tree[k<<1|1].sum+=tree[k<<1|1].len*lz;tree[k<<1].lazy+=lz;tree[k<<1|1].lazy+=lz;}}void build(int k,int l,int r){tree[k].l=l;tree[k].r=r;tree[k].len=r-l+1;tree[k].sum=tree[k].lazy=0;if(l==r)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);}void update(int k,int l,int r,LL val){if(tree[k].l>r||tree[k].r<l)return;if(tree[k].r<=r&&tree[k].l>=l){tree[k].sum+=tree[k].len*val;tree[k].lazy+=val;return;}pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);}LL query(int k,int l,int r){if(tree[k].l>r||tree[k].r<l)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].sum;pushdown(k);return query(k<<1,l,r)+query(k<<1|1,l,r);} }namespace SAM {int tot,last,pos[N],pre[N];struct Node{int ch[26];int fa,len;}st[N<<1];inline int newnode(){tot++;for(int i=0;i<26;i++)st[tot].ch[i]=0;st[tot].fa=st[tot].len=0;return tot;}void add(int x){int p=last,np=last=newnode();st[np].len=st[p].len+1;while(p&&!st[p].ch[x])st[p].ch[x]=np,p=st[p].fa;if(!p)st[np].fa=1;else{int q=st[p].ch[x];if(st[p].len+1==st[q].len)st[np].fa=q;else{int nq=newnode();st[nq]=st[q]; st[nq].len=st[p].len+1;st[q].fa=st[np].fa=nq;while(p&&st[p].ch[x]==q)st[p].ch[x]=nq,p=st[p].fa;//向上把所有q都替換成nq}}}void init(){last=1;tot=0;newnode();}void build(){init();int len=strlen(s+1);for(int i=1;i<=len;i++){add(s[i]-'a');pos[i]=last;}} }namespace LCT {int fa[N],ch[N][2],pre[N],min_len[N],len[N],Stack[N],lazy[N],top;bool son(int x)//判斷點(diǎn)x是父節(jié)點(diǎn)的左兒子還是右兒子 {return x==ch[fa[x]][1];}bool isroot(int x)//判斷一個(gè)點(diǎn)是不是根節(jié)點(diǎn)(當(dāng)前splay的根節(jié)點(diǎn)){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}void pushup(int x)//上傳(維護(hù)信息){min_len[x]=min(len[x],min(min_len[ch[x][0]],min_len[ch[x][1]]));}void update_lazy(int x,int lz){pre[x]=lazy[x]=lz;}void pushdown(int x)//下傳(更新反轉(zhuǎn)標(biāo)記用){if(lazy[x]){update_lazy(ch[x][0],lazy[x]);update_lazy(ch[x][1],lazy[x]);lazy[x]=0;}}void rotate(int x)//splay的旋轉(zhuǎn) {int y=fa[x],z=fa[y],c=son(x);ch[y][c]=ch[x][c^1];if(ch[y][c]) fa[ch[y][c]]=y;fa[x]=z;if(!isroot(y))ch[z][son(y)]=x;ch[x][c^1]=y;fa[y]=x;pushup(y);}void splay(int x)//將x轉(zhuǎn)到根節(jié)點(diǎn) {Stack[top=1]=x;for (int i=x;!isroot(i);i=fa[i])Stack[++top]=fa[i];while (top) pushdown(Stack[top--]);for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])if (!isroot(y)) son(x)^son(y)?rotate(x):rotate(y);pushup(x);}void access(int x,int id)//把x節(jié)點(diǎn)到x所在樹(連通塊)的根節(jié)點(diǎn)之間的路徑全部變成重路徑{int y;for(y=0;x;y=x,x=fa[x]){splay(x),ch[x][1]=y,pushup(x);if(pre[x])Seg::update(1,pre[x]-SAM::st[x].len+1,pre[x]-min_len[x]+1,-1);}splay(y);update_lazy(y,id);Seg::update(1,1,id,1);}void build(){min_len[0]=inf;for(int i=1;i<=SAM::tot;i++){fa[i]=SAM::st[i].fa;len[i]=min_len[i]=SAM::st[SAM::st[i].fa].len+1;lazy[i]=ch[i][0]=ch[i][1]=pre[i]=0;}} };int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);scanf("%s",s+1);int n=strlen(s+1);SAM::build();LCT::build();Seg::build(1,1,n);int m;scanf("%d",&m);for(int i=1;i<=m;i++){int l,r;scanf("%d%d",&l,&r);q[r].emplace_back(i,l);}for(int i=1;i<=n;i++){LCT::access(SAM::pos[i],i);for(auto it:q[i])ans[it.first]=Seg::query(1,it.second,i);}for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);return 0; }

    ?

    總結(jié)

    以上是生活随笔為你收集整理的洛谷 - P6292 区间本质不同子串个数(SAM+LCT+线段树)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。