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 imes nb)的,时间复杂度三方,喜提10pts。

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

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

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

然后对于每个A,B串,就可以从反串(n-l+1)位置对应的节点向上跳,找到串对应的节点(u)并标记,(u)满足(len[slink[u]]<r-l+1le 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
",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;
}

原文地址:https://www.cnblogs.com/fruitea/p/12392921.html