Luogu5284 [十二省联考2019]字符串问题
Luogu5284 [十二省联考2019]字符串问题
\(SAM\)+拓扑
回来赶一篇博客吧。
\(SAM\)简单题。
很容易发现,对于一个串\(A_i\)对应的\(B\)集合中每一个\(B_j\),都能使得\(A_i\)转移到含有前缀\(B_j\)的串\(A_k\),如果出现了正环,那么答案必然是无限的,同时我们建立的图中不含有负权边,我们可以直接判断边集中是否有环即可,这可以用\(Tarjan\)线性完成。
如何找到含有一个串\(T\)的前缀的所有\(A_i\)串?我们考虑对反串建立\(SAM\),那么需要满足\(T\)为\(A_i\)的后缀,找到\(T\)所在的节点,他的\(parent\)树上子树就是满足条件的\(A\)串。
其实我们只需要向其子树连一条\(0\)边即可,注意一个节点包含一个长度区间内的串,我们可以考虑进行拆点,那么直接跑最长路即可。
我好像没有发现这一点,结果对着\(parent\)树的\(dfn\)序来了一个线段树优化建图。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#define N 200005
#define M 4000005
#define ll long long
#define IT vector<node> :: iterator
using namespace std;
int T;
int n,na,nb,m,x,y,l,r,ed[N];
int A[N],B[N],val[N];
int tot=1,lst=1,tr[N << 1][26],len[N << 1],pre[N << 1];
char s[N];
struct edge
{
int nxt,v;
edge (int Nxt=0,int V=0)
{
nxt=Nxt,v=V;
}
}e[N << 1];
int tt,fr[N << 1];
int f[N << 1][22];
int cnt,dfn[N],rdfn[N];
int idfn[N],odfn[N];
struct node
{
int op,len,id;
node (int O=0,int L=0,int I=0)
{
op=O,len=L,id=I;
}
bool operator < (const node &A) const
{
if (len!=A.len)
return len<A.len;
return op>A.op;
}
};
vector<node>E[N << 1];
struct Cedge
{
int nxt,v,cost;
Cedge (int Nxt=0,int V=0,int Cost=0)
{
nxt=Nxt,v=V,cost=Cost;
}
}se[(N << 2)+M];
int mxp,pt[N];
int stt,sfr[N << 2],rd[N << 2];
bool flag=true,vis[N << 2],insta[N << 2];
ll ans=0,dis[N << 2];
queue<int>q;
void ins(int c,int I)
{
int p=lst,q,np;
ed[I]=lst=np=++tot;
len[np]=len[p]+1;
for (;p && !tr[p][c];p=pre[p])
tr[p][c]=np;
if (!p)
pre[np]=1; else
{
q=tr[p][c];
if (len[p]+1==len[q])
pre[np]=q; else
{
int g=++tot;
memcpy(tr[g],tr[q],sizeof(tr[q]));
len[g]=len[p]+1,pre[g]=pre[q];
for (;p && tr[p][c]==q;p=pre[p])
tr[p][c]=g;
pre[np]=pre[q]=g;
}
}
}
void add(int x,int y)
{
++tt;
e[tt]=edge(fr[x],y),fr[x]=tt;
}
void sadd(int x,int y,int z)
{
++stt;
se[stt]=Cedge(sfr[x],y,z),sfr[x]=stt;
++rd[y];
}
void dfs(int u)
{
for (int i=fr[u];i;i=e[i].nxt)
{
int v=e[i].v;
f[v][0]=u;
dfs(v);
}
}
void GetDfn(int u)
{
sort(E[u].begin(),E[u].end());
for (IT it=E[u].begin();it!=E[u].end();++it)
if (it->op==1)
{
dfn[++cnt]=it->id;
rdfn[it->id]=cnt;
} else
idfn[it->id]=cnt+1;
for (int i=fr[u];i;i=e[i].nxt)
{
int v=e[i].v;
GetDfn(v);
}
for (IT it=E[u].begin();it!=E[u].end();++it)
if (it->op==2)
odfn[it->id]=cnt;
}
void build(int p,int l,int r)
{
mxp=max(mxp,p);
if (l==r)
{
pt[dfn[l]]=p;
return;
}
int mid=(l+r) >> 1;
build(p << 1,l,mid);
build(p << 1 | 1,mid+1,r);
if (l==mid)
sadd(p,p << 1,val[dfn[l]]); else
sadd(p,p << 1,0);
if (mid+1==r)
sadd(p,p << 1 | 1,val[dfn[r]]); else
sadd(p,p << 1 | 1,0);
}
void link(int p,int l,int r,int x,int y,int z)
{
if (l==x && r==y)
{
if (l==r)
sadd(z,p,val[dfn[l]]); else
sadd(z,p,0);
return;
}
int mid=(l+r) >> 1;
if (y<=mid)
link(p << 1,l,mid,x,y,z); else
if (x>mid)
link(p << 1 | 1,mid+1,r,x,y,z); else
{
link(p << 1,l,mid,x,mid,z);
link(p << 1 | 1,mid+1,r,mid+1,y,z);
}
}
void Tarjan(int u)
{
vis[u]=insta[u]=true;
for (int i=sfr[u];i && flag;i=se[i].nxt)
{
int v=se[i].v;
if (!vis[v])
Tarjan(v); else
if (insta[v])
{
flag=false;
return;
}
}
insta[u]=false;
}
void Topo()
{
if (mxp==1)
dis[1]=val[1];
for (int i=1;i<=mxp;++i)
if (!rd[i])
q.push(i);
while (!q.empty())
{
int u=q.front();
q.pop();
for (int i=sfr[u];i;i=se[i].nxt)
{
int v=se[i].v,cost=se[i].cost;
dis[v]=max(dis[v],dis[u]+cost);
--rd[v];
if (!rd[v])
q.push(v);
}
}
}
void Clear()
{
memset(tr,0,26*(tot+1)*sizeof(int));
memset(fr,0,(tot+1)*sizeof(int));
memset(sfr,0,(mxp+1)*sizeof(int));
memset(vis,0,(mxp+1)*sizeof(bool));
memset(insta,0,(mxp+1)*sizeof(bool));
memset(dis,0,(mxp+1)*sizeof(ll));
memset(rd,0,(mxp+1)*sizeof(int));
for (int i=1;i<=tot;++i)
E[i].clear();
flag=true;
stt=tt=cnt=mxp=0;
tot=lst=1;
ans=0;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%s",s+1);
n=strlen(s+1);
reverse(s+1,s+n+1);
for (int i=1;i<=n;++i)
ins(s[i]-'a',i);
for (int i=2;i<=tot;++i)
add(pre[i],i);
dfs(1);
for (int j=1;j<=20;++j)
for (int i=1;i<=tot;++i)
f[i][j]=f[f[i][j-1]][j-1];
scanf("%d",&na);
for (int i=1;i<=na;++i)
{
scanf("%d%d",&l,&r);
l=n-l+1,r=n-r+1,swap(l,r);
int k=ed[r];
for (int j=20;j>=0;--j)
if (f[k][j] && r-len[pre[f[k][j]]]<l)
k=f[k][j];
if (r-len[pre[k]]<l)
k=f[k][0];
A[i]=k,val[i]=r-l+1;
E[k].push_back(node(1,r-l+1,i));
}
scanf("%d",&nb);
for (int i=1;i<=nb;++i)
{
scanf("%d%d",&l,&r);
l=n-l+1,r=n-r+1,swap(l,r);
int k=ed[r];
for (int j=20;j>=0;--j)
if (f[k][j] && r-len[pre[f[k][j]]]<l)
k=f[k][j];
if (r-len[pre[k]]<l)
k=f[k][0];
B[i]=k;
E[k].push_back(node(2,r-l+1,i));
}
GetDfn(1);
build(1,1,na);
scanf("%d",&m);
for (int i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
if (idfn[y]<=odfn[y])
link(1,1,na,idfn[y],odfn[y],pt[x]);
}
for (int i=1;i<=mxp && flag;++i)
if (!vis[i])
Tarjan(i);
if (flag)
{
Topo();
for (int i=1;i<=mxp;++i)
ans=max(ans,dis[i]);
printf("%lld\n",ans);
} else
puts("-1");
Clear();
}
return 0;
}