LOJ #3049. 「十二省聯考 2019」字符串問題
阿新 • • 發佈:2019-04-07
ack push continue back name != stream vector mem
LOJ #3049. 「十二省聯考 2019」字符串問題
https://loj.ac/problem/3049
題意:給你\(na\)個\(A\)類串,\(nb\)個\(B\)類串,\(m\)組支配關系,求一個長度很長的串\(t_1t_2...t_k\)滿足
\(t_i\)為\(A\)類串,\(t_i\)能支配一個\(B\)類串,使得該\(B\)類串為\(t_{i+1}\)的前綴。
分析:
- 一個簡單的暴力就是枚舉\(A_i\)後面能接的\(A_j\)進行連邊,然後拓撲序求一下最長路。
- 很難優化,我們發現這相當於在中間塞入一個\(B\)串,然後發現如果答案不是無窮的話,每種\(B\)類串最多只用一次,那麽我們可以對於每個支配關系連邊,再由\(B\)
- 對反串建立\(sam\),可以發現\(B\)串向\(parent\)樹的一個子樹上連邊,倍增定位可以做到一個\(log\)。
- 考慮\(B\)串可能大於A串的情況,這樣的話就很可能兩個串在同一個節點上但長度不同,我們可以對於每個\(A,B\)額外開出一個節點\(p\), \(p\rightarrow A\),\(B\rightarrow p\), \(p\)連一個前綴出來,總結點數是\(6n\)級別的吧。
代碼:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <bitset> #include <vector> #include <iostream> using namespace std; typedef long long ll; #define N 1200050 #define M 5000050 #define db(x) cerr<<#x<<" = "<<x<<endl char w[N]; int n,la,lb,al[N],ar[N],bl[N],br[N],m,xx[N],yy[N]; int head[N],to[M],nxt[M],cnt,Q[N],du[N],tot,rt; int ch[N][26],fa[N],len[N],uuz,pos[N],lst=1,f[21][N],ln[N],Lst[N]; ll g[N]; bool cmp(const int &x,const int &y) { return ln[x]==ln[y]?x<y:ln[x]>ln[y]; } vector<int>V[N]; inline void add(int u,int v) { to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; du[v]++; } void insert(int x,int id) { int p=lst,np=++uuz,q,nq; len[np]=len[p]+1; lst=np; for(;p&&!ch[p][x];p=fa[p]) ch[p][x]=np; if(!p) fa[np]=rt; else { q=ch[p][x]; if(len[q]==len[p]+1) fa[np]=q; else { nq=++uuz; fa[nq]=fa[q]; len[nq]=len[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); fa[q]=fa[np]=nq; for(;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq; } } } void dfs(int x) { int i; for(i=1;(1<<i)<=uuz;i++) f[i][x]=f[i-1][f[i-1][x]]; for(i=head[x];i;i=nxt[i]) { f[0][to[i]]=x; dfs(to[i]); } } int find(int l,int r) { int p=pos[r]; int i; for(i=20;i>=0;i--) if(f[i][p]&&len[f[i][p]]>=r-l+1) p=f[i][p]; return p; } void solve() { scanf("%s",w+1); n=strlen(w+1); reverse(w+1,w+n+1); int i,j; ll ans=0; scanf("%d",&la); for(i=1;i<=la;i++) scanf("%d%d",&al[i],&ar[i]); scanf("%d",&lb); for(i=1;i<=lb;i++) scanf("%d%d",&bl[i],&br[i]); scanf("%d",&m); tot=la+lb; for(i=1;i<=la;i++) { al[i]=n-al[i]+1,ar[i]=n-ar[i]+1,swap(al[i],ar[i]); } for(i=1;i<=lb;i++) { bl[i]=n-bl[i]+1,br[i]=n-br[i]+1,swap(bl[i],br[i]); } for(i=1;i<=la;i++) ln[i]=ar[i]-al[i]+1; for(i=1;i<=lb;i++) ln[i+la]=br[i]-bl[i]+1; rt=tot+1;lst=tot+1;uuz=tot+1; for(i=1;i<=n;i++) insert(w[i]-'a',i),pos[i]=lst; for(i=rt+1;i<=uuz;i++) add(fa[i],i); dfs(rt); for(i=rt;i<=uuz;i++) head[i]=0,du[i]=0; cnt=0; for(i=1;i<=m;i++) { scanf("%d%d",&xx[i],&yy[i]); add(xx[i],yy[i]+la); } for(i=1;i<=la;i++) { int p=find(al[i],ar[i]); V[p].push_back(i); } for(i=1;i<=lb;i++) { int p=find(bl[i],br[i]); V[p].push_back(i+la); } int ee=uuz; for(i=rt+1;i<=uuz;i++) { sort(V[i].begin(),V[i].end(),cmp); int lim=V[i].size(),lst=i; Lst[i]=i; if(lim==0) continue; for(j=0;j<lim;j++) { ee++; add(ee,lst); lst=ee; if(V[i][j]<=la) add(ee,V[i][j]); else add(V[i][j],ee); } Lst[i]=lst; } for(i=rt+1;i<=uuz;i++) add(fa[i],Lst[i]); uuz=ee; int l=0,r=0; for(i=1;i<=uuz;i++) if(!du[i]) Q[r++]=i; while(l<r) { int x=Q[l++]; g[x]+=(x<=la?(ar[x]-al[x]+1):0); ans=max(ans,g[x]); for(i=head[x];i;i=nxt[i]) { du[to[i]]--; g[to[i]]=max(g[to[i]],g[x]); if(!du[to[i]]) Q[r++]=to[i]; } } if(r!=uuz) puts("-1"); else printf("%lld\n",ans); cnt=0; for(i=1;i<=n;i++) pos[i]=0; for(i=1;i<=uuz;i++) { fa[i]=len[i]=head[i]=du[i]=g[i]=Q[i]=0; V[i].clear(); } for(i=0;(1<<i)<=uuz;i++) for(j=rt;j<=uuz;j++) f[i][j]=0; for(i=rt;i<=uuz;i++) memset(ch[i],0,sizeof(ch[i])); return ; } int main() { int T; scanf("%d",&T); while(T--) solve(); }
LOJ #3049. 「十二省聯考 2019」字符串問題