Luogu5284 [十二省联考2019]字符串问题

在神my的督促下更一点垃圾做的垃圾题。

Link

Solution

考虑用图的形式描述A与B的关系:

\(A_i\)支配\(B_j\)就连边\(A_i->B_j\)

\(B_i\)\(A_j\)的前缀就连边\(B_i->A_j\)

然后设A权值为串长,B权值为0,在这个图上拓扑序即可。(ps:无限长当且仅当图中有环,反映出来就是跑完拓扑序之后图中还有点入度不为0,这样判就不需要再跑一边dfs判环了)

但是这样暴力建边边数是\(m+na\times nb\)的,时间复杂度三方,喜提10pts。

考虑优化前缀关系的连边。

后缀树就可以很好的描述字符串的前缀关系(简化Trie)。

所以对反串建SAM,同时记录反串每个位置对应的节点,即平时“SAM上节点endpos代表元(插入时的pos)”的反向映射。

然后对于每个A,B串,就可以从反串\(n-l+1\)位置对应的节点向上跳,找到串对应的节点\(u\)并标记,\(u\)满足\(len[slink[u]]<r-l+1\le len[u]\)。这个过程可以倍增加速到\(O(nlogn)\)

但是直接利用SAM原图+\(m\)条新边跑拓扑是错的。因为SAM的节点代表的是一段长度区间,同一个节点上,可能会出现\(|A_j|<|B_i|\)的情况,这个时候\(B_i,A_j\)并不满足前缀关系连边的条件。所以还需要拆点,同一个SAM上节点拆成其对应的A,B串的多个点。

连边就利用前缀优化连边(不知道是不是这么叫):一个节点上的串按长度排序后,原点向第一个B之前的区间连边,每个B向下一个B之前的那段区间全都连边,最后一个B(没有B就是原点)向\(parent\)树的子节点连边。

然后上拓扑随便跑跑就完事了!!1

Code

#include<bits/stdc++.h>
using namespace std;
#define REP(i,a,b) for(int i=(a),_ed=(b);i<=_ed;++i)
#define DREP(i,a,b) for(int i=(a),_ed=(b);i>=_ed;--i)
#define pb(x) push_back((x))
typedef long long ll;
inline int read(){
    register int x=0,f=1;register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
    return f?x:-x;
}

const int N=2e5+5,NN=N<<1,M=N<<2;
int n,na,nb,m;
char s[N];
vector<int> E[M],S[NN];
int tot,las,trans[NN][26],slink[NN],len[NN],pos[N];
int tot2,ord[NN],cnt[N],fa[NN][20],rola[N],rolb[N];
int L[NN],val[M],in[M],is[M];
queue<int> q;ll ans,f[M];

inline void extend(int c){
    int p=las,np=las=++tot;
    len[np]=len[p]+1;
    for(;p&&!trans[p][c];p=slink[p])trans[p][c]=np;
    if(!p)slink[np]=1;
    else{
    int q=trans[p][c];
    if(len[q]==len[p]+1)slink[np]=q;
    else{
        int nq=++tot;memcpy(trans[nq],trans[q],sizeof trans[q]);
        len[nq]=len[p]+1;slink[nq]=slink[q];slink[np]=slink[q]=nq;
        for(;p&&trans[p][c]==q;p=slink[p])trans[p][c]=nq;
    }
    }
}

inline int find(int p,int l){DREP(i,19,0)if(len[fa[p][i]]>=l)p=fa[p][i];return p;}
inline void add(int u,int v){++in[v];E[u].pb(v);}
inline bool cmp(const int &a,const int &b){return val[a]==val[b]?is[a]<is[b]:val[a]<val[b];}

inline void work(){
    scanf("%s",s+1);n=strlen(s+1);
    DREP(i,n,1)extend(s[i]-'a'),pos[n-i+1]=las;
    REP(i,1,tot)++cnt[len[i]];
    REP(i,1,n)cnt[i]+=cnt[i-1];
    DREP(i,tot,1)ord[cnt[len[i]]--]=i;
    REP(i,1,tot){
    int u=ord[i];fa[u][0]=slink[u];
    for(int i=1;fa[u][i-1];++i)fa[u][i]=fa[fa[u][i-1]][i-1];
    }
    tot2=tot;
    REP(i,1,na=read()){
    int l=n-read()+1,r=n-read()+1;
    int p=find(pos[l],l-r+1);
    val[rola[i]=++tot2]=l-r+1;S[p].pb(tot2);
    is[tot2]=1;
    }
    REP(i,1,nb=read()){
    int l=n-read()+1,r=n-read()+1;
    int p=find(pos[l],l-r+1);
    val[rolb[i]=++tot2]=l-r+1;S[p].pb(tot2);
    }
    REP(i,1,m=read()){
    int u=rola[read()],v=rolb[read()];
    add(u,v);
    }
    REP(i,1,tot){
    sort(S[i].begin(),S[i].end(),cmp);
    int lst=i;
    REP(j,0,S[i].size()-1){
        int u=S[i][j];add(lst,u);
        if(!is[u])lst=u,val[u]=0;
    }
    L[i]=lst;
    }
    REP(i,2,tot)add(L[slink[ord[i]]],ord[i]);
    REP(i,1,tot2)if(!in[i])q.push(i);
    while(!q.empty()){
    int u=q.front();q.pop();
    f[u]+=val[u];
    REP(i,0,E[u].size()-1){
        int v=E[u][i];
        f[v]=max(f[v],f[u]);
        if(!--in[v])q.push(v);
    }
    }
    REP(i,1,tot2){
    ans=max(ans,f[i]);
    if(in[i])return puts("-1"),void();
    }
    printf("%lld\n",ans);
}

inline void clear(){
    memset(pos+1,0,sizeof(int)*n);
    memset(cnt+1,0,sizeof(int)*n);
    memset(rola+1,0,sizeof(int)*na);
    memset(rolb+1,0,sizeof(int)*nb);
    REP(i,1,tot){
    slink[i]=len[i]=L[i]=ord[i]=0;
    S[i].clear();
    memset(fa[i],0,sizeof(fa[0]));
    memset(trans[i],0,sizeof(trans[0]));
    }
    tot=las=1;
    REP(i,1,tot2){
    val[i]=in[i]=is[i]=f[i]=0;
    E[i].clear();
    }
    ans=0;
    q=queue<int>();
}

int main(){
    //freopen("in.in","r",stdin);
    REP(i,1,read())
    clear(),
    work();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fruitea/p/12392921.html