P5284 [十二省联考2019]字符串问题(技巧,SAM的前缀和连边)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P5284 [十二省联考2019]字符串问题(技巧,SAM的前缀和连边)相关的知识,希望对你有一定的参考价值。
考虑每个 A i A_i Ai向 A j A_j Aj连边,当且仅当存在一个被 A i A_i Ai支配的串 B z B_z Bz,使得 B z B_z Bz为 A j A_j Aj的前缀
这样得到了一个图,只需对这个图拓扑排序求最长路即可.
T 1 T1 T1
先可以对所有的支配关系由 A A A连向 B B B,费用为 0 0 0(此时 B B B串相当于一个中转点)
那么现在只需要考虑 B i B_i Bi是哪些 A A A串的前缀然后连边
可以很容易的知道,在后缀自动机上,父亲是儿子的后缀
如果我们对反串建 S A M SAM SAM,反串 S A M SAM SAM中父亲是儿子的后缀,意味着原串中父亲是儿子的前缀
所以只需要由 B i B_i Bi向所有子树中的 A A A串节点连边即可
此时复杂度降为 O ( n 2 ) O(n^2) O(n2),究其原因还是边太多了不能暴力连
T 2 T2 T2
注意到 S A M SAM SAM中的一个节点对应多个子串,我们把这些子串按照长度排序
若长度相同,则按 A A A类串在前, B B B类串在后的顺序排序
设排序后变成这个样子 a 1 , a 2 , b 1 , a 3 , b 2 , a 4 a_1,a_2,b_1,a_3,b_2,a_4 a1,a2,b1,a3,b2,a4
那么我们就连边 ( i , a 1 ) , ( i , a 2 ) , ( i , b 1 ) , ( b 1 , a 3 ) , ( b 1 , b 2 ) , ( b 2 , a 4 ) (i,a_1),(i,a_2),(i,b_1),(b_1,a_3),(b_1,b_2),(b_2,a_4) (i,a1),(i,a2),(i,b1),(b1,a3),(b1,b2),(b2,a4)
其中 i i i表示 S A M SAM SAM中的这个节点,并无实际意义,只是起到祖先和儿子的连通作用
设祖先有个 B B B类串能转移到 a 1 a_1 a1,那么也必然能转移到 a 2 a_2 a2,因为他们互为前缀关系
然后,祖先中的 B B B类串也可以不选择 a 1 , a 2 a_1,a_2 a1,a2,而是转移到 b 1 b_1 b1,在下面的串再找机会
所以我们的连边策略就是记录一个 l a s t b lastb lastb表示上次的最长 B B B类串
然后 l a s t b lastb lastb向遇到的所有串都连边,直到遇到更长的 B B B类串再更新 l a s t b lastb lastb
非常厉害的技巧,类似前缀和连边
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e6+10;
int zi[maxn][27],fa[maxn],l[maxn],ed[maxn],id = 1, las = 1;
char s[maxn];
int le,m,na,nb;
void insert(int c)
{
int p = las, np = ++id; las = id;
l[np] = l[p]+1;
for( ; p&&zi[p][c]==0;p=fa[p] ) zi[p][c] = np;
if( !p ) fa[np] = 1;
else
{
int q = zi[p][c];
if( l[q]==l[p]+1 ) fa[np] = q;
else
{
int nq = ++id;
memcpy( zi[nq],zi[q],sizeof zi[q] );
fa[nq] = fa[q], l[nq] = l[p]+1;
fa[np] = fa[q] = nq;
for( ; p&&zi[p][c]==q;p=fa[p] ) zi[p][c] = nq;
}
}
}
vector<int>vec[maxn];
int up[maxn][22],in[maxn],lst[maxn],isa[maxn],sz;
int a[maxn],b[maxn];
ll dis[maxn];
bool com(int a,int b)
{
return l[a]>l[b]||(l[a]==l[b]&&isa[a]>isa[b] );
}
void isok(int aorb)
{
int L,R; scanf("%d%d",&L,&R);
int x = ed[L];
for(int i=21;i>=0;i--)
if( up[x][i] && l[up[x][i]]>=R-L+1 ) x = up[x][i];
isa[++sz] = aorb, l[sz] = R-L+1;
vec[x].push_back( sz ); in[sz]++;
}
void find_A_B()
{
sz = id;
scanf("%d",&na );
for(int i=1;i<=na;i++) isok( 1 ),a[i] = sz;
scanf("%d",&nb );
for(int i=1;i<=nb;i++) isok( 0 ),b[i] = sz;
for(int i=1;i<=id;i++) sort( vec[i].begin(),vec[i].end(),com );
for(int i=1;i<=id;i++)
{
int last = i;
for(int j=vec[i].size()-1;j>=0;j--)
{
int now = vec[i][j];
vec[last].push_back( now ); in[now]++;
if( !isa[now] ) last = now;
}
lst[i] = last;
}
for(int i=2;i<=id;i++)
vec[lst[fa[i]]].push_back( i ),in[i]++;
for(int i=1;i<=sz;i++)
if( !isa[i] ) l[i] = 0;
scanf("%d",&m );
for(int i=1;i<=m;i++)
{
int L,R; scanf("%d%d",&L,&R );
vec[a[L]].push_back( b[R] ); in[b[R]]++;
}
}
void tuopu()
{
queue<int>q;
for(int i=1;i<=sz;i++)
if( !in[i] ) q.push( i );
int shu = 0; ll ans = 0;
while( !q.empty() )
{
int u = q.front(); q.pop();
shu++; ans = max( ans,dis[u]+l[u] );
for(auto v:vec[u] )
luogu P5284 [十二省联考2019]字符串问题