版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/89385542
题目:
https://www.luogu.org/problemnew/show/P5284
分析:
考虑怎样构造一个合法串。我们从每一个
类串向他支配的
类串连边,
类串向以他为前缀的
类串连边,形成一个有向图。每一个
类串权值设为他的
,
类串设为0。那么一条路径的权值和就是某个合法串的长度。
显然相当于求图最长链,存在环则解无限大,不然可以拓扑排序求。
考虑
类串连向
类串的边。显然要满足
且
。
考虑以
和
开头的后缀,显然他们的
。
对原串先跑出后缀数组。
我们先把所有串按长度从大到小排序,可以维护一棵可持久化线段树维护长度在
的
类串,线段下标为该串的
。对于串
来说,
的在rank上是一个单峰函数,二分求得左右边界。相当于
在
内的比
长的串都是可以连边的,向对应线段树节点连边。因为要不断插入更短的串,所以前后线段树不一样,要可持久化。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define LL long long
const int maxn=2e5+7;
using namespace std;
int T,na,nb,m,len,tot,cnt,root,x,y;
int ls[maxn*30],val[maxn*30],r[maxn*30];
LL f[maxn*30],ans;
char s[maxn];
queue <int> q;
struct rec{
int l,r,num;
}a[maxn],b[maxn];
struct node{
int l,r;
}t[maxn*30];
struct edge{
int y,next;
}g[maxn*60];
struct suffle_array{
int x[maxn],y[maxn],c[maxn],lg[maxn],rank[maxn],sa[maxn],h[maxn][20];
void getsa()
{
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
memset(rank,0,sizeof(rank));
memset(sa,0,sizeof(sa));
memset(h,0,sizeof(h));
int n=len,m=1000;
for (int i=1;i<=n;i++) lg[i]=trunc(log(i+0.5)/log(2));
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) x[i]=s[i];
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=1;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>0;i--) sa[c[x[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int num=0;
for (int i=n-k+1;i<=n;i++) y[++num]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
for (int i=1;i<=m;i++) c[i]=0;
for (int i=1;i<=n;i++) c[x[i]]++;
for (int i=1;i<=m;i++) c[i]+=c[i-1];
for (int i=n;i>0;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
num=1;
x[sa[1]]=1;
for (int i=2;i<=n;i++)
{
if ((y[sa[i]]!=y[sa[i-1]]) || (y[sa[i]+k]!=y[sa[i-1]+k]))
{
x[sa[i]]=++num;
}
else x[sa[i]]=num;
}
if (num>=n) break;
m=num;
}
for (int i=1;i<=n;i++) rank[i]=x[i];
}
void getheight()
{
int n=len,k=0;
for (int i=1;i<=n;i++)
{
if (k) k--;
int j=sa[rank[i]-1];
while ((i+k<=n) && (j+k<=n) && (s[i+k]==s[j+k])) k++;
h[rank[i]][0]=k;
}
int c=1;
for (int j=1;j<20;j++)
{
for (int i=1;i+2*c-1<=n;i++) h[i][j]=min(h[i][j-1],h[i+c][j-1]);
c<<=1;
}
}
int lcp(int x,int y)
{
x=rank[x],y=rank[y];
if (x>y) swap(x,y);
x++;
int c=lg[y-x+1];
return min(h[x][c],h[y-(1<<c)+1][c]);
}
int getleft(int x,int k)
{
int l=1,r=x-1,ans=x;
while (l<=r)
{
int mid=(l+r)/2;
if (lcp(sa[mid],sa[x])>=k) r=mid-1,ans=mid;
else l=mid+1;
}
return ans;
}
int getright(int x,int k)
{
int l=x+1,r=len,ans=x;
while (l<=r)
{
int mid=(l+r)/2;
if (lcp(sa[mid],sa[x])>=k) l=mid+1,ans=mid;
else r=mid-1;
}
return ans;
}
}sa;
bool cmp(rec a,rec b)
{
return a.r-a.l>b.r-b.l;
}
void add(int x,int y)
{
g[++tot]=(edge){y,ls[x]};
ls[x]=tot;
}
void ins(int &p,int q,int l,int r,int x,int num)
{
p=++cnt;
if (l==r)
{
if (q) add(p+na+nb,q+na+nb);
add(p+na+nb,num);
return;
}
int mid=(l+r)/2;
if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,num);
else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,num);
if (t[p].l) add(p+na+nb,t[p].l+na+nb);
if (t[p].r) add(p+na+nb,t[p].r+na+nb);
}
void solve(int p,int l,int r,int x,int y,int num)
{
if (!p) return;
if ((l==x) && (r==y))
{
add(num,p+na+nb);
return;
}
int mid=(l+r)/2;
if (y<=mid) solve(t[p].l,l,mid,x,y,num);
else if (x>mid) solve(t[p].r,mid+1,r,x,y,num);
else
{
solve(t[p].l,l,mid,x,mid,num);
solve(t[p].r,mid+1,r,mid+1,y,num);
}
}
void gettop()
{
int n=na+nb+cnt,m=tot;
for (int i=1;i<=n;i++) r[i]=0;
for (int i=1;i<=m;i++) r[g[i].y]++;
int num=0;
for (int i=1;i<=n;i++)
{
if (!r[i])
{
f[i]=(LL)val[i];
q.push(i);
num++;
}
}
ans=0;
memset(f,0,sizeof(f));
while (!q.empty())
{
int x=q.front();
q.pop();
ans=max(ans,f[x]);
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (r[y])
{
f[y]=max(f[y],f[x]+(LL)val[y]);
r[y]--;
if (!r[y]) q.push(y),num++;
}
}
}
if (num==n) printf("%lld\n",ans);
else printf("-1\n");
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%s",s+1);
len=strlen(s+1);
sa.getsa();
sa.getheight();
memset(val,0,sizeof(val));
scanf("%d",&na);
for (int i=1;i<=na;i++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].num=i;
val[i]=a[i].r-a[i].l+1;
}
scanf("%d",&nb);
for (int i=1;i<=nb;i++)
{
scanf("%d%d",&b[i].l,&b[i].r);
b[i].num=i;
}
sort(a+1,a+na+1,cmp);
sort(b+1,b+nb+1,cmp);
scanf("%d",&m);
memset(ls,0,sizeof(ls));
tot=0;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y+na);
}
cnt=root=0;
for (int i=1,j=1;j<=nb;j++)
{
while ((a[i].r-a[i].l>=b[j].r-b[j].l) && (i<=na))
{
ins(root,root,1,len,sa.rank[a[i].l],a[i].num);
i++;
}
int L=sa.getleft(sa.rank[b[j].l],b[j].r-b[j].l+1);
int R=sa.getright(sa.rank[b[j].l],b[j].r-b[j].l+1);
solve(root,1,len,L,R,b[j].num+na);
}
gettop();
}
}