[BJWC2018]Border 的四種求法
阿新 • • 發佈:2019-01-12
轉化 取值 class 查詢 sca new stdin 離線 inf 中最大的\(i\)。
且\(i-len[x]+1\le l\)的最大的\(i\),
此時線段樹上二分,根據右子樹的最小值是否\(\le l\)作出決策,單次時間復雜度降為\(O(logn)\)。
由於每個點到根節點的輕邊僅\(O(log)\)條,因此插入的總時間復雜度為\(O(nlog^2n)\)。
線段樹處理詢問的總時間同樣為\(O(nlog^2n)\)。
故總時間復雜度為\(O(nlog^2n)\)。
description
luogu
給一個小寫字母字符串\(S\),\(q\)次詢問每次給出\(l,r\),求\(s[l..r]\)的\(Border\)。
solution
我們考慮轉化題面:給定\(l,r\),求滿足\(lcs(i,r)\ge i-l+1\)的最大的\(i\)。
對於\(lcs(i,r)\),考慮對\(S\)構建\(SAM\),那麽我們知道\(lcs\)的可能取值就是從後綴\(r\)所代表的節點沿著\(fail\)樹到根節點的路徑上節點的\(len\)的取值。
那麽我們得到了一個暴力做法:\(SAM\)+線段樹合並後暴跳\(fail\)樹,
在每個節點的線段樹中查詢\([l,min(r,len+l-1))\)
如何優化?
考慮樹鏈剖分,我們要求出的一條根到父親的鏈在\(fail\)樹上是若幹段重鏈的前綴。
那麽可以對每條重鏈進行離線而分開考慮。
查詢前綴時,除了最後一個點需要查詢重兒子及其子樹,其余的點都只要查詢輕兒子及其子樹即可。
因此查詢前綴時對於最後一個點仍然使用\(SAM\)+線段樹合並的做法,
對於其他的點,考慮將\(lcs(i,r)\ge i-l+1\)變為\(i-len[x]+1\le l\)
只須從上往下一個個插入輕子樹的兒子+回答詢問。
回答詢問時,需要建立一棵以\(i\)為下標,權值為\(i-len[x]+1\)的最小值線段樹,
查詢時需要查詢\(l\le i<r\)
此時線段樹上二分,根據右子樹的最小值是否\(\le l\)作出決策,單次時間復雜度降為\(O(logn)\)。
由於每個點到根節點的輕邊僅\(O(log)\)條,因此插入的總時間復雜度為\(O(nlog^2n)\)。
線段樹處理詢問的總時間同樣為\(O(nlog^2n)\)。
故總時間復雜度為\(O(nlog^2n)\)。
code
話說這是我第一次寫SAM+線段樹合並
我錯了是我人醜常數大1e5還要跑300ms
#include<bits/stdc++.h> #define pb push_back #define mp make_pair #define fi first #define se second #define FL "a" using namespace std; typedef unsigned long long ll; typedef pair<int,int> PI; typedef vector<int> VI; typedef long double dd; const int N=4e5+10; const int mod=1e9+7; const int inf=2147483647; inline ll read(){ ll data=0,w=1;char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar(); return data*w; } inline void file(){ freopen(FL".in","r",stdin); freopen(FL".out","w",stdout); } #define ls (i<<1) #define rs (i<<1|1) #define mid ((l+r)>>1) struct node{int l,r,id;}; int n,q;char s[N];vector<node>Q[N];int ans[N]; int head[N],nxt[N],to[N],cnt; inline void add(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;} int lst=1,tot=1,cnts=0,len[N],fa[N],tr[N][26],pos[N],fid[N]; inline void extend(int c){ int cur=++tot,u=lst;len[cur]=len[lst]+1; while(u&&!tr[u][c])tr[u][c]=cur,u=fa[u]; if(!u)fa[cur]=1; else{ int v=tr[u][c]; if(len[v]==len[u]+1)fa[cur]=v; else{ int clone=++tot;len[clone]=len[u]+1; memcpy(tr[clone],tr[v],sizeof(tr[clone])); fa[clone]=fa[v];fa[v]=fa[cur]=clone; while(u&&tr[u][c]==v)tr[u][c]=clone,u=fa[u]; } } lst=cur;pos[lst]=++cnts;fid[cnts]=lst; } namespace s1{ int rt[N],s[2][20*N],tot=0; void Insert(int &i,int l,int r,int p){ i=++tot;s[0][i]=s[1][i]=0;if(l==r)return; p<=mid?Insert(s[0][i],l,mid,p):Insert(s[1][i],mid+1,r,p); } int Merge(int a,int b){ if(!a||!b)return a|b;int c=++tot; s[0][c]=Merge(s[0][a],s[0][b]); s[1][c]=Merge(s[1][a],s[1][b]); return c; } int Query(int i,int l,int r,int x,int y){ if(!i)return 0;if(l==r)return l; if(mid<y&&s[1][i]){ int res=Query(s[1][i],mid+1,r,x,y); return res?res:Query(s[0][i],l,mid,x,y); } else return x<=mid?Query(s[0][i],l,mid,x,y):0; } inline void insert(int u){Insert(rt[u],1,n,pos[u]);} inline void merge(int u,int v){rt[u]=Merge(rt[u],rt[v]);} inline int query(int u,int l,int r){return Query(rt[u],1,n,l,r);} void print(int i,int l,int r){ if(!i)return;if(l==r){printf("%d ",l);return;} print(s[0][i],l,mid);print(s[1][i],mid+1,r); } } namespace s2{ int mn[N<<2],tot; void build(int i,int l,int r){ mn[i]=inf;if(l==r)return;build(ls,l,mid);build(rs,mid+1,r); } void del(int i,int l,int r,int p){ mn[i]=inf;if(l==r)return; p<=mid?del(ls,l,mid,p):del(rs,mid+1,r,p); } void insert(int i,int l,int r,int p,int v){ if(p<l||p>r)return;mn[i]=min(mn[i],v);if(l==r)return; p<=mid?insert(ls,l,mid,p,v):insert(rs,mid+1,r,p,v); } int query(int i,int l,int r,int x,int y,int v){ if(mn[i]>v)return 0;if(l==r)return mn[i]<=v?l:0; if(mid<y&&mn[rs]<=v){ int res=query(rs,mid+1,r,x,y,v); return res?res:query(ls,l,mid,x,y,v); } else return x<=mid?query(ls,l,mid,x,y,v):0; } } int sz[N],dep[N],son[N],top[N]; void dfs1(int u){ sz[u]=1;dep[u]=dep[fa[u]]+1;son[u]=0; for(int i=head[u],v;i;i=nxt[i]){ dfs1(v=to[i]);sz[u]+=sz[v]; if(sz[son[u]]<sz[v])son[u]=v; } } void dfs2(int u,int tp){ top[u]=tp;if(pos[u])s1::insert(u); if(son[u])dfs2(son[u],tp),s1::merge(u,son[u]); for(int i=head[u],v;i;i=nxt[i]) if((v=to[i])!=son[u])dfs2(v,v),s1::merge(u,v); } void ins(int u,int len){ if(pos[u])s2::insert(1,1,n,pos[u],pos[u]-len+1); for(int i=head[u],v;i;i=nxt[i])ins(v=to[i],len); } void del(int u){ if(pos[u])s2::del(1,1,n,pos[u]); for(int i=head[u],v;i;i=nxt[i])del(v=to[i]); } inline void getans(int x){ vector<int>a;a.clear();int m=0; for(;x;x=son[x])a.pb(x),m++; for(int k=0,u;k<m;k++){ u=a[k]; if(pos[u])s2::insert(1,1,n,pos[u],pos[u]-len[u]+1); for(int i=head[u],v;i;i=nxt[i]){ v=to[i];if(v==son[u])continue;ins(v,len[u]); } for(int i=0,sz=Q[u].size(),l,r,id,res;i<sz;i++){ l=Q[u][i].l;r=Q[u][i].r;id=Q[u][i].id; res=s2::query(1,1,n,l,r-1,l); if(l<=res&&res<r)ans[id]=max(ans[id],res-l+1); res=s1::query(u,l,min(len[u]+l-1,r-1)); if(l<=res&&res<r)ans[id]=max(ans[id],res-l+1); } } for(int k=0,u;k<m;k++){ u=a[k];if(pos[u])s2::del(1,1,n,pos[u]); for(int i=head[u],v;i;i=nxt[i]){ v=to[i];if(v==son[u])continue;del(v); } } for(int k=0,u;k<m;k++){ u=a[k]; for(int i=head[u],v;i;i=nxt[i]){ v=to[i];if(v==son[u])continue; getans(v); } } } inline void init(){ scanf("%s",s+1);n=strlen(s+1); for(int i=1;i<=n;i++)extend(s[i]-'a'); for(int i=2;i<=tot;i++)add(fa[i],i); dfs1(1);dfs2(1,1);s2::build(1,1,n); } inline void solve(){ q=read(); for(int i=1,l,r,u;i<=q;i++){ l=read();r=read();u=fid[r]; while(u)Q[u].pb((node){l,r,i}),u=fa[top[u]]; } getans(1); for(int i=1;i<=q;i++)printf("%d\n",ans[i]); } int main() { init(); solve(); return 0; }
[BJWC2018]Border 的四種求法