1. 程式人生 > >【BZOJ5496】[十二省聯考2019]字符串問題(後綴樹)

【BZOJ5496】[十二省聯考2019]字符串問題(後綴樹)

cmp href 成了 mes add cst push 過去 %s

【BZOJ5496】[十二省聯考2019]字符串問題(後綴樹)

題面

BZOJ
洛谷

題解

首先顯然可以把具有支配關系的串從\(A\)\(B\)連一條有向邊,如果\(B_i\)\(A_j\)的前綴,就從\(B\)連一條邊到\(A\)。這樣子問題就轉化成了要求解這個二分圖的最長路經,有環答案就是\(-1\)
然後顯然就是要找個什麽東西出來優化連邊是吧。。。
現在唯一要處理的東西就是要找到個啥玩意,來優化這個滿足前綴條件的連邊。
假裝我們有一個所有後綴都被插進去的\(Trie\)樹,那麽對於每一個\(B\)只需要找到其對應的節點,然後它子樹中的每一個\(A\)都會被他連過去,這樣子似乎就達成了優化連邊,即每一個\(B\)

連向這個節點,然後這樣節點連向在這個節點終止的\(A\)
這個復雜度顯然是爆炸的,所以我們可以直接建立後綴樹,這樣子節點數就被優化到了\(O(n)\)級別。
於是問題又出現了,在後綴樹上的一個節點表示的長度是一段區間,假如一個節點上又掛了\(A\),又掛了\(B\)就會出鍋。(雖然不管這個也有\(80\)分了)
那行啊,我們來拆個點,每個樹上節點拆兩個,一個\(u\)負責掛好所有兒子,另外一個\(v\)負責掛好所有在這個點的\(A\),然後所有的\(A\)按照長度從小往大掛成一條鏈。然後\(v\)指向\(u\),這樣子任何一個\(B\)對應的一定是一段後綴\(A\),所以直接後綴優化連邊連向這條鏈,然後再連向\(u\)
表示指向所有的兒子。
這樣子還是很麻煩,實際上有一個更加優秀的做法,就是對於一個點如果掛了多個串,那麽就按照每個串長把這個點強行拆掉就行了(雖然本質上就是掛了一條鏈)。
於是只需要求解最長路就行了,而且還是樹上的最長鏈,只需要按照拓撲序做就行了,即使有環也可以在這個過程中處理掉。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 800800
#define pb push_back
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
vector<int> E[MAX],W[MAX],str[MAX];
char ch[MAX];int Len;
int fa[20][MAX],pos[MAX],val[MAX];
struct Node{int son[26],len,ff;void clear(){memset(son,0,sizeof(son));len=ff=0;}}t[MAX];
int last=1,tot=1;
void extend(int c)
{
    int p=last,np=++tot;last=np;
    t[np].len=t[p].len+1;
    while(p&&!t[p].son[c])t[p].son[c]=np,p=t[p].ff;
    if(!p)t[np].ff=1;
    else
    {
        int q=t[p].son[c];
        if(t[q].len==t[p].len+1)t[np].ff=q;
        else
        {
            int nq=++tot;
            t[nq]=t[q];t[nq].len=t[p].len+1;
            t[q].ff=t[np].ff=nq;
            while(p&&t[p].son[c]==q)t[p].son[c]=nq,p=t[p].ff;
        }
    }
}
vector<int> ee[MAX];
void dfs(int u,int ff)
{
    fa[0][u]=ff;
    for(int i=1;i<20;++i)fa[i][u]=fa[i-1][fa[i-1][u]];
    for(int i=0,l=ee[u].size();i<l;++i)dfs(ee[u][i],u);
}
void Work()
{
    Len=strlen(ch+1);
    for(int i=Len;i;--i)extend(ch[i]-97),pos[i]=last;
    for(int i=1;i<=tot;++i)ee[t[i].ff].pb(i);
    dfs(1,0);
}
int TOT,nd[MAX],IDA[MAX],IDB[MAX],Lim[MAX],lenA[MAX];
void Add(int u,int v,int w){if(u)E[u].pb(v),W[u].pb(w);}
bool cmp(int a,int b){return lenA[a]<lenA[b];}
void Build(int u,int ff)
{
    int np=++TOT;nd[u]=np;Lim[np]=t[u].len;
    if(ff)
    {
        int lst=ff;
        for(int i=0,l=str[u].size();i<l;++i)
        {
            Add(lst,++TOT,0),val[TOT]=lenA[str[u][i]],fa[0][TOT]=lst,Lim[TOT]=lenA[str[u][i]],lst=TOT;
            IDA[str[u][i]]=TOT;
        }
        Add(lst,np,0);fa[0][np]=lst;
    }
    for(int i=0,l=ee[u].size();i<l;++i)Build(ee[u][i],np);
}
int deg[MAX];ll dis[MAX],ans;
void Topsort()
{
    for(int i=1;i<=TOT;++i)
        for(int j=0,l=E[i].size();j<l;++j)
            deg[E[i][j]]+=1;
    queue<int> Q;int QwQ=0;
    for(int i=1;i<=TOT;++i)if(!deg[i])Q.push(i);
    while(!Q.empty())
    {
        int u=Q.front();Q.pop();QwQ+=1;
        ans=max(ans,dis[u]+val[u]);
        for(int i=0,l=E[u].size();i<l;++i)
        {
            int v=E[u][i];
            dis[v]=max(dis[v],dis[u]+W[u][i]);
            if(!--deg[v])Q.push(v);
        }
    }
    if(QwQ<TOT)puts("-1");
    else printf("%lld\n",ans);
}
int main()
{
    int T=read();
    while(T--)
    {
        scanf("%s",ch+1);
        Work();
        int A=read();
        for(int i=1;i<=A;++i)
        {
            int l=read(),r=read(),len=r-l+1;
            int u=pos[l];lenA[i]=len;
            for(int i=19;~i;--i)
                if(t[fa[i][u]].len>=len)u=fa[i][u];
            str[u].pb(i);
        }
        for(int i=1;i<=tot;++i)sort(str[i].begin(),str[i].end(),cmp);
        for(int i=0;i<=tot;++i)
            for(int j=0;j<20;++j)fa[j][i]=0;
        Build(1,0);
        for(int j=1;j<20;++j)
            for(int i=1;i<=TOT;++i)
                fa[j][i]=fa[j-1][fa[j-1][i]];
        int B=read();
        for(int i=1;i<=B;++i)
        {
            int l=read(),r=read(),len=r-l+1;IDB[i]=++TOT;
            int u=nd[pos[l]];
            for(int j=19;~j;--j)
                if(Lim[fa[j][u]]>=len)u=fa[j][u];
            Add(IDB[i],u,0);
        }
        int C=read();
        while(C--)
        {
            int x=read(),y=read();
            Add(IDA[x],IDB[y],lenA[x]);
        }
        Topsort();
        for(int i=0;i<=TOT;++i)
        {
            ee[i].clear();E[i].clear();t[i].clear();
            str[i].clear();W[i].clear();
            for(int j=0;j<20;++j)fa[j][i]=0;
            IDA[i]=IDB[i]=Lim[i]=pos[i]=deg[i]=0;
            dis[i]=ans=val[i]=lenA[i]=nd[i]=0;
        }
        last=tot=1;TOT=0;
    }
    return 0;
}

【BZOJ5496】[十二省聯考2019]字符串問題(後綴樹)