【题解】Luogu P5284 [十二省联考2019]字符串问题

时间:2022-07-25 20:34:42

原题传送门

我用sa做的本题 (码量似乎有点大)

先对原串建sa

考虑如何建图:

一开始每个以某个后缀都单独用一个链表存储。用merge函数依次将lcp为n~1的后缀的链表合并,当这个lcp为某个a串时候,在链表中插入a。当这个lcp为某个b串时候,以b开始的后缀所在的链表中的元素就是其所对应的a串(见merging函数)

现在每个b对应的a的编号都是链表中一段连续的区间

珂以用线段树优化建图

最后跑一下拓扑排序即可得出答案

#include <bits/stdc++.h>
#define N 1000005
#define M 16000005
#define ll long long
using namespace std;
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register ll x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
inline ll Max(register ll a,register ll b)
{
return a>b?a:b;
}
struct edge{
int to,next;
}e[M];
int head[N],cnte=1,degree[N],w[N],nt;
inline void add_edge(register int u,register int v)
{
e[++cnte]=(edge){v,head[u]};
head[u]=cnte;
++degree[v];
}
inline void cleargraph()
{
memset(w,0,sizeof(w));
memset(degree,0,sizeof(degree));
memset(head,0,sizeof(head));
cnte=1;
}
ll dp[N];
int q[N],qh,qt;
inline ll topsort()
{
qh=qt=0;
memset(dp,0,sizeof(dp));
for(register int i=1;i<=nt;++i)
if(!degree[i])
q[++qt]=i;
ll ans=0;
while(qh<qt)
{
int u=q[++qh];
dp[u]+=w[u];
ans=Max(ans,dp[u]);
for(register int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
dp[v]=Max(dp[v],dp[u]);
if(!--degree[v])
q[++qt]=v;
}
}
return qt<nt?-1LL:ans;
}
int n,sizem;
char s[N];
int rak[N],sa[N],tp[N],tex[N],height[N];
inline void Qsort()
{
for(register int i=0;i<=sizem;++i)
tex[i]=0;
for(register int i=1;i<=n;++i)
++tex[rak[i]];
for(register int i=1;i<=sizem;++i)
tex[i]+=tex[i-1];
for(register int i=n;i>=1;--i)
sa[tex[rak[tp[i]]]--]=tp[i];
}
inline void sa_build()
{
memset(tp,0,sizeof(tp));
memset(rak,0,sizeof(rak));
sizem=30;
for(register int i=1;i<=n;++i)
rak[i]=s[i]-'a'+1,tp[i]=i;
Qsort();
for(register int w=1,p=0;p<n;sizem=p,w<<=1)
{
p=0;
for(register int i=1;i<=w;++i)
tp[++p]=n-w+i;
for(register int i=1;i<=n;++i)
if(sa[i]>w)
tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=p=1;
for(register int i=2;i<=n;++i)
rak[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
}
}
inline void getheight()
{
int k=0;
for(register int i=1;i<=n;++i)
{
if(k)
--k;
int j=sa[rak[i]-1];
while(s[i+k]==s[j+k])
++k;
height[rak[i]]=k;
}
}
struct Unionset{
int f[N];
inline void makeset(register int n)
{
for(register int i=1;i<=n;++i)
f[i]=i;
}
inline int find(register int x)
{
return f[x]==x?f[x]:f[x]=find(f[x]);
}
inline void merge(register int x,register int y)
{
if(find(x)!=find(y))
f[f[x]]=f[y];
}
};
struct ListNode{
int l,r;
ListNode():l(-1),r(-1){}
};
ListNode* listNode=nullptr;
inline void clearListNodes()
{
if(listNode!=nullptr)
delete[] listNode;
listNode=new ListNode[N];
}
struct List{
int head,tail;
List():head(-1),tail(-1){}
List(int node):head(node),tail(node){}
List(int l,int r):head(l),tail(r){}
List operator +(const List& other){
if(tail==-1)
return other;
if(other.head==-1)
return *this;
listNode[tail].r=other.head;
listNode[other.head].l=tail;
return List(head,other.tail);
}
};
int na,nb,la[N],ra[N],lb[N],rb[N];
List lst[N];
vector<int> merges[N],alen[N],blen[N];
Unionset us;
int invl[N],invr[N],ppos[N],seq[N];
inline void merging()
{
clearListNodes();
for(register int i=1;i<=n;++i)
lst[i]=List();
for(register int i=0;i<=n;++i)
{
merges[i].clear();
alen[i].clear();
blen[i].clear();
}
for(register int i=1;i<n;++i)
merges[height[i+1]].push_back(i);
for(register int i=1;i<=na;++i)
alen[ra[i]-la[i]+1].push_back(i);
for(register int i=1;i<=nb;++i)
blen[rb[i]-lb[i]+1].push_back(i);
us.makeset(n);
for(register int len=n;len>=0;--len)
{
for(register int i=0;i<merges[len].size();++i)
{
int k=merges[len][i];
int u=us.find(k),v=us.find(k+1);
List tmp=lst[u]+lst[v];
us.merge(u,v);
lst[us.find(u)]=tmp;
}
for(register int i=0;i<alen[len].size();++i)
{
int a=alen[len][i];
int u=us.find(rak[la[a]]);
lst[u]=List(a)+lst[u];
}
for(register int i=0;i<blen[len].size();++i)
{
int b=blen[len][i];
int v=us.find(rak[lb[b]]);
invl[b]=Max(0,lst[v].head);
invr[b]=Max(0,lst[v].tail);
}
}
for(register int i=1,u=lst[us.find(1)].head;i<=na;++i,u=listNode[u].r)
{
if(u==-1)
break;
seq[i]=u;
ppos[u]=i;
}
ppos[0]=-1;
for(register int i=1;i<=nb;++i)
{
invl[i]=ppos[invl[i]];
invr[i]=ppos[invr[i]];
}
}
inline void input()
{
scanf("%s",s+1);
n=strlen(s+1);
na=read();
for(register int i=1;i<=na;++i)
la[i]=read(),ra[i]=read();
nb=read();
for(register int i=1;i<=nb;++i)
lb[i]=read(),rb[i]=read();
cleargraph();
int m=read();
for(register int i=1;i<=m;++i)
{
int x=read(),y=read();
add_edge(x,na+y);
}
}
int tot=0,ls[N],rs[N];
inline void seg_build(register int &x,register int l,register int r)
{
if(l==r)
{
x=seq[l];
return;
}
else
x=++tot;
int mid=l+r>>1;
seg_build(ls[x],l,mid);
seg_build(rs[x],mid+1,r);
add_edge(x,ls[x]),add_edge(x,rs[x]);
}
inline void seg_addedge(register int x,register int l,register int r,register int b,register int L,register int R)
{
if(L<=l&&r<=R)
{
add_edge(b+na,x);
return;
}
int mid=l+r>>1;
if(L<=mid)
seg_addedge(ls[x],l,mid,b,L,R);
if(R>mid)
seg_addedge(rs[x],mid+1,r,b,L,R);
}
inline void buildsegtr()
{
tot=na+nb;
int root;
seg_build(root,1,na);
for(register int i=1;i<=nb;++i)
{
if(invl[i]<0)
continue;
seg_addedge(root,1,na,i,invl[i],invr[i]);
}
nt=tot;
}
inline ll solve()
{
input();
sa_build();
getheight();
merging();
buildsegtr();
for(register int i=1;i<=na;++i)
w[i]=ra[i]-la[i]+1;
return topsort();
}
int T;
int main()
{
T=read();
while(T--)
write(solve()),puts("");
return 0;
}