在神my的督促下更一点垃圾做的垃圾题。
Solution
考虑用图的形式描述A与B的关系:
\(A_i\)支配\(B_j\)就连边\(A_i->B_j\)
\(B_i\)是\(A_j\)的前缀就连边\(B_i->A_j\)
然后设A权值为串长,B权值为0,在这个图上拓扑序即可。(ps:无限长当且仅当图中有环,反映出来就是跑完拓扑序之后图中还有点入度不为0,这样判就不需要再跑一边dfs判环了)
但是这样暴力建边边数是\(m+na\times nb\)的,时间复杂度三方,喜提10pts。
考虑优化前缀关系的连边。
后缀树就可以很好的描述字符串的前缀关系(简化Trie)。
所以对反串建SAM,同时记录反串每个位置对应的节点,即平时“SAM上节点endpos代表元(插入时的pos)”的反向映射。
然后对于每个A,B串,就可以从反串\(n-l+1\)位置对应的节点向上跳,找到串对应的节点\(u\)并标记,\(u\)满足\(len[slink[u]]<r-l+1\le len[u]\)。这个过程可以倍增加速到\(O(nlogn)\)。
但是直接利用SAM原图+\(m\)条新边跑拓扑是错的。因为SAM的节点代表的是一段长度区间,同一个节点上,可能会出现\(|A_j|<|B_i|\)的情况,这个时候\(B_i,A_j\)并不满足前缀关系连边的条件。所以还需要拆点,同一个SAM上节点拆成其对应的A,B串的多个点。
连边就利用前缀优化连边(不知道是不是这么叫):一个节点上的串按长度排序后,原点向第一个B之前的区间连边,每个B向下一个B之前的那段区间全都连边,最后一个B(没有B就是原点)向\(parent\)树的子节点连边。
然后上拓扑随便跑跑就完事了!!1
Code
#include<bits/stdc++.h>
using namespace std;
#define REP(i,a,b) for(int i=(a),_ed=(b);i<=_ed;++i)
#define DREP(i,a,b) for(int i=(a),_ed=(b);i>=_ed;--i)
#define pb(x) push_back((x))
typedef long long ll;
inline int read(){
register int x=0,f=1;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
return f?x:-x;
}
const int N=2e5+5,NN=N<<1,M=N<<2;
int n,na,nb,m;
char s[N];
vector<int> E[M],S[NN];
int tot,las,trans[NN][26],slink[NN],len[NN],pos[N];
int tot2,ord[NN],cnt[N],fa[NN][20],rola[N],rolb[N];
int L[NN],val[M],in[M],is[M];
queue<int> q;ll ans,f[M];
inline void extend(int c){
int p=las,np=las=++tot;
len[np]=len[p]+1;
for(;p&&!trans[p][c];p=slink[p])trans[p][c]=np;
if(!p)slink[np]=1;
else{
int q=trans[p][c];
if(len[q]==len[p]+1)slink[np]=q;
else{
int nq=++tot;memcpy(trans[nq],trans[q],sizeof trans[q]);
len[nq]=len[p]+1;slink[nq]=slink[q];slink[np]=slink[q]=nq;
for(;p&&trans[p][c]==q;p=slink[p])trans[p][c]=nq;
}
}
}
inline int find(int p,int l){DREP(i,19,0)if(len[fa[p][i]]>=l)p=fa[p][i];return p;}
inline void add(int u,int v){++in[v];E[u].pb(v);}
inline bool cmp(const int &a,const int &b){return val[a]==val[b]?is[a]<is[b]:val[a]<val[b];}
inline void work(){
scanf("%s",s+1);n=strlen(s+1);
DREP(i,n,1)extend(s[i]-'a'),pos[n-i+1]=las;
REP(i,1,tot)++cnt[len[i]];
REP(i,1,n)cnt[i]+=cnt[i-1];
DREP(i,tot,1)ord[cnt[len[i]]--]=i;
REP(i,1,tot){
int u=ord[i];fa[u][0]=slink[u];
for(int i=1;fa[u][i-1];++i)fa[u][i]=fa[fa[u][i-1]][i-1];
}
tot2=tot;
REP(i,1,na=read()){
int l=n-read()+1,r=n-read()+1;
int p=find(pos[l],l-r+1);
val[rola[i]=++tot2]=l-r+1;S[p].pb(tot2);
is[tot2]=1;
}
REP(i,1,nb=read()){
int l=n-read()+1,r=n-read()+1;
int p=find(pos[l],l-r+1);
val[rolb[i]=++tot2]=l-r+1;S[p].pb(tot2);
}
REP(i,1,m=read()){
int u=rola[read()],v=rolb[read()];
add(u,v);
}
REP(i,1,tot){
sort(S[i].begin(),S[i].end(),cmp);
int lst=i;
REP(j,0,S[i].size()-1){
int u=S[i][j];add(lst,u);
if(!is[u])lst=u,val[u]=0;
}
L[i]=lst;
}
REP(i,2,tot)add(L[slink[ord[i]]],ord[i]);
REP(i,1,tot2)if(!in[i])q.push(i);
while(!q.empty()){
int u=q.front();q.pop();
f[u]+=val[u];
REP(i,0,E[u].size()-1){
int v=E[u][i];
f[v]=max(f[v],f[u]);
if(!--in[v])q.push(v);
}
}
REP(i,1,tot2){
ans=max(ans,f[i]);
if(in[i])return puts("-1"),void();
}
printf("%lld\n",ans);
}
inline void clear(){
memset(pos+1,0,sizeof(int)*n);
memset(cnt+1,0,sizeof(int)*n);
memset(rola+1,0,sizeof(int)*na);
memset(rolb+1,0,sizeof(int)*nb);
REP(i,1,tot){
slink[i]=len[i]=L[i]=ord[i]=0;
S[i].clear();
memset(fa[i],0,sizeof(fa[0]));
memset(trans[i],0,sizeof(trans[0]));
}
tot=las=1;
REP(i,1,tot2){
val[i]=in[i]=is[i]=f[i]=0;
E[i].clear();
}
ans=0;
q=queue<int>();
}
int main(){
//freopen("in.in","r",stdin);
REP(i,1,read())
clear(),
work();
return 0;
}