[十二省联考2019]字符串问题:后缀数组+主席树优化建图

分析

蒟蒻不会\(SAM\),只好来一发主席树优化建图的题解。

\(N\)为原字符串的长度。首先我们考虑一个最基本的思路,我们发现\(A_j\)能接在\(A_i\)后面当且仅当存在一个\(B_k\)\(A_i\)支配且是\(A_j\)的前缀。考虑建图,如果\(A_i\)支配\(B_j\),那么从\(A_i\)\(B_j\)连一条单向边,如果\(B_i\)\(A_j\)的前缀,那么从\(B_i\)\(A_j\)连一条单向边。所有的\(A_i\)有权值,权值为这个串\(A_i\)的长度。那么跑一个最长链就好了,如果有环输出\(-1\)(根据题意不可能有\(0\)环)。

考虑一个\(80\)分的做法,即保证了\(|A_i| \geq |B_j|\)的部分。我们发现前面所述的做法的连边数量是\(O(N^2)\)级别的,门槛在“如果\(B_i\)\(A_j\)的前缀”这一部分的连边。考虑到如果保证了\(|A_i| \geq |B_j|\),那么“\(B_i\)\(A_j\)的前缀”这一条件等价于\(\text{lcp}(S(lb_i,N),S(la_j,N)) \geq |B_j|\)。这样转化有什么用呢?如果我们把所有的\(A\)串按照\(S(la,N)\)进行后缀排序的话,会发现所有满足“\(B_i\)\(A_j\)的前缀”的\(A_j\)是一段连续的区间,那么就可以使用线段树优化建图了。

考虑满分做法,如果\(|A_i| \geq |B_j|\)这个条件不再满足,那么“\(B_i\)\(A_j\)的前缀”这一条件等价于\(\text{lcp}(S(lb_i,N),S(la_j,N)) \geq |B_j|\)\(|A_j| \geq |B_i|\),即和前面相比多了一维的限制,这一维的限制我们可以使用主席树代替线段树处理掉。

\(N,N_a,N_b\)同阶,则时间复杂度和空间复杂度均为\(O(TN \log N)\)

点我查看万恶之源

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
#include <utility>
#include <vector>
#include <queue>
#include <set>
#include <map>

#define rin(i,a,b) for(int i=(a);i<=(b);++i)
#define irin(i,a,b) for(int i=(a);i>=(b);--i)
#define trav(i,a) for(int i=head[a];i;i=e[i].nxt)
#define Size(a) (int)a.size()
#define pb push_back
#define lowbit(a) ((a)&(-(a)))
typedef long long LL;

using std::cerr;
using std::endl;

inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

const int MAXN=200005;

int n,m,na,nb,b[MAXN],l[MAXN],pos[MAXN],pos2[MAXN],odr[MAXN];
int siz,sa[MAXN],rk[MAXN<<1],sc[MAXN<<1],bk[MAXN],ht[MAXN],st[20][MAXN];
int ecnt,tot,root[MAXN],lc[MAXN*20],rc[MAXN*20],head[MAXN*20],deg[MAXN*20],w[MAXN*20],loc,ql,qr;
char s[MAXN];
LL f[MAXN*20];
std::queue<int> q;

struct node{
    int x,id,len;
}a[MAXN];

struct Edge{
    int to,nxt;
}e[MAXN*50];

inline void add_edge(int bg,int ed){
    ++ecnt;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    head[bg]=ecnt;
    ++deg[ed];
}

void radix_sort(){
    rin(i,1,siz)bk[i]=0;
    rin(i,1,n)++bk[rk[i]];
    rin(i,1,siz)bk[i]+=bk[i-1];
    irin(i,n,1)sa[bk[rk[sc[i]]]--]=sc[i];
}

void suffix_sort(){
    siz=26;
    rin(i,1,n)rk[i]=s[i],sc[i]=i;
    radix_sort();
    for(int wd=1;;wd<<=1){
        int cnt=0;
        rin(i,1,wd)sc[++cnt]=n-wd+i;
        rin(i,1,n)if(sa[i]-wd>0)sc[++cnt]=sa[i]-wd;
        radix_sort();
        std::swap(rk,sc);
        rk[sa[1]]=cnt=1;
        rin(i,2,n)rk[sa[i]]=(sc[sa[i-1]]==sc[sa[i]]&&sc[sa[i-1]+wd]==sc[sa[i]+wd]?cnt:++cnt);
        siz=cnt;
        if(cnt==n)return;
    }
}

inline void calc_height(){
    int preh=0;
    rin(i,1,n){
        if(rk[i]==1){
            preh=ht[rk[i]]=0;
            continue;
        }
        int now=std::max(preh-1,0);
        while(s[sa[rk[i]-1]+now]==s[i+now])++now;
        preh=ht[rk[i]]=now;
    }
}

void build_st(){
    rin(i,1,n)st[0][i]=ht[i];
    int lim=log2(n);
    rin(i,1,lim)rin(j,1,n-(1<<i)+1)
        st[i][j]=std::min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}

inline int lcp(int x,int y){
    if(x==y)return n-x+1;
    x=rk[x],y=rk[y];
    if(x>y)std::swap(x,y);
    ++x;int lim=log2(y-x+1);
    return std::min(st[lim][x],st[lim][y-(1<<lim)+1]);
}

inline bool cmp(node x,node y){
    return rk[x.x]<rk[y.x];
}

inline bool cmp2(int x,int y){
    return a[x].len>a[y].len;
}

#define mid ((l+r)>>1)

int ins(int pre,int l,int r){
    int o=++tot;
    lc[o]=lc[pre],rc[o]=rc[pre];
    if(l==r){
        pos[l]=o;
        w[o]=a[l].len;
        return o;
    }
    if(loc<=mid)lc[o]=ins(lc[pre],l,mid);
    else rc[o]=ins(rc[pre],mid+1,r);
    add_edge(o,lc[o]);
    add_edge(o,rc[o]);
    return o;
}

void conn(int o,int l,int r,int frm){
    if(ql>qr)return;
    if(!o)return;
    if(ql<=l&&r<=qr){
        add_edge(frm,o);
        return;
    }
    if(mid>=ql)conn(lc[o],l,mid,frm);
    if(mid<qr)conn(rc[o],mid+1,r,frm);
}

#undef mid

LL topo(){
    int cnt=0;LL ret=0;
    while(!q.empty())q.pop();
    rin(i,1,tot)if(!deg[i])q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();++cnt;
        f[x]+=w[x];
        ret=std::max(ret,f[x]);
        trav(i,x){
            int ver=e[i].to;
            f[ver]=std::max(f[ver],f[x]);
            --deg[ver];
            if(!deg[ver])q.push(ver);
        }
    }
    if(cnt<tot)return -1;
    else return ret;
}

void clear(){
    ecnt=tot=0;
    memset(head,0,sizeof head);
    memset(deg,0,sizeof deg);
    memset(w,0,sizeof w);
    memset(f,0,sizeof f);
}

int main(){
    int T=read();
    while(T--){
        clear();
        scanf("%s",s+1);
        n=strlen(s+1);
        rin(i,1,n)s[i]-='a'-1;
        suffix_sort();
        calc_height();
        build_st();
        na=read();
        rin(i,1,na){
            int la=read(),ra=read();
            a[i]=(node){la,i,ra-la+1};
        }
        std::sort(a+1,a+na+1,cmp);
        rin(i,1,na)pos2[a[i].id]=i,odr[i]=i;
        std::sort(odr+1,odr+na+1,cmp2);
        root[n+1]=0;int ptr=0;
        irin(i,n,1){
            root[i]=root[i+1];
            while(ptr<na&&a[odr[ptr+1]].len==i){
                loc=odr[++ptr];
                root[i]=ins(root[i],1,na);
            }
        }
        nb=read();
        rin(i,1,nb){
            int lb=read(),rb=read();
            b[i]=lb,l[i]=rb-lb+1;
            w[tot+i]=0;
        }
        rin(i,1,nb){
            int ll=1,rr=std::upper_bound(a+1,a+na+1,(node){b[i],0,0},cmp)-a-1,ret=rr+1;
            while(ll<=rr){
                int midd=((ll+rr)>>1);
                if(lcp(a[midd].x,b[i])>=l[i])ret=midd,rr=midd-1;
                else ll=midd+1;
            }
            ql=ret;
            ll=std::lower_bound(a+1,a+na+1,(node){b[i],0,0},cmp)-a,rr=na,ret=ll-1;
            while(ll<=rr){
                int midd=((ll+rr)>>1);
                if(lcp(a[midd].x,b[i])>=l[i])ret=midd,ll=midd+1;
                else rr=midd-1;
            }
            qr=ret;
            conn(root[l[i]],1,na,tot+i);
        }
        m=read();
        rin(i,1,m){
            int u=read(),v=read();
            add_edge(pos[pos2[u]],tot+v);
        }
        tot+=nb;
        printf("%lld\n",topo());
    }
    return 0;
}

posted on 2019-04-08 10:11 ErkkiErkko 阅读(...) 评论(...) 编辑 收藏

统计