题解 P5284 【[十二省联考2019]字符串问题】

cosmicAC

2019-04-08 13:20:54

Solution

下面是我的后缀数组解法。 首先考虑前80分的做法,和其他所有题解一样,是要把“T的分割中,y可作为x的后一个串”这个关系用有向图表示出来,然后只要判断是不是DAG。如果是那么跑最长路,否则-1。这里判DAG其实可以和求拓扑序一起做,如果跑完拓扑序之后还有未到达的点的话就不是DAG。 然后考虑建图方法。枚举一个A串,枚举被A串支配的所有B串,由于前80分中的$\forall{i,j},|A_i|\ge|B_j|$限制,"存在一个前缀是B的串组成的集合"是后缀数组上的一个区间,起始点在区间中的所有A串都是合法的。只要对这个区间中所有的A串连边即可。找区间的时候可以在height数组上建ST表倍增(或二分),复杂度都是一个log的。 然后是建边,暴力建边的话边数是$n^2$级别的肯定不行。然而可以利用ST表建边,比如区间是$[l,r]$,$k=\lfloor\log_2{(r-l+1)}\rfloor$,就向表示$[l,l+2^k),[r+1-2^k,r]$的两个点连边即可。这样就可以得到80分。 然后是满分算法。可以看出,唯一的区别是开始位置相同的一些A串中,只有较长的一部分串可以连边。这就是二维偏序了,我在考场上以为要树套树所以不可做。后来发现用不着,可以交换一下两维,使得第一维是A串长度,第二维是A串开始位置的rank。这样第一维就变成了前缀,所以可以用主席树优化建图,点数和边数的规模都是一个log的。 可惜由于常数远大于SAM,洛谷过不去。loj上可以通过的代码: ```cpp #include<bits/stdc++.h> using namespace std; using ll=long long; const int maxn=200010; char s[maxn];ll dis[maxn*25]; int n,sa[maxn],nr[maxn*2],rnk[maxn*2],st[maxn][20],hd[maxn*25],p[maxn],ind[maxn*25]; int T,tot,m,na,nb,w[maxn*25],l[maxn],r[maxn],que[maxn*25],rt[maxn],ps[maxn]; struct stt{int l,r,id;}a[maxn];vector<stt> u; bool operator<(stt a,stt b){return a.r-a.l>b.r-b.l;} vector<int> v[maxn]; struct edge{int v,nxt;}e[maxn*100]; void adde(int a,int b){e[++m]={b,hd[a]};hd[a]=m;ind[b]++;} int qry1(int p,int vl){ for(int k=19;~k;k--)if(p>=1<<k&&st[p-(1<<k)+1][k]>=vl)p-=1<<k; return p; } int qry2(int p,int vl){ for(int k=19;~k;k--)if(p+(1<<k)<=n+1&&st[p][k]>=vl)p+=1<<k; return p-1; } struct segTree{ int L[maxn*25],R[maxn*25]; void ins(int id,int p,int tl=1,int tr=n){ if(tl==tr){adde(++tot,p);u.push_back({tot,id});return;} int mid=tl+tr>>1; if(rnk[a[id].l]<=mid)ins(id,L[p],tl,mid),R[++tot]=R[p],L[tot]=tot-1; else ins(id,R[p],mid+1,tr),L[++tot]=L[p],R[tot]=tot-1; adde(tot,p),adde(tot,tot-1); } void add(int l,int r,int x,int p,int tl=1,int tr=n){ if(l<=tl&&tr<=r){adde(x,p);return;} int mid=tl+tr>>1; if(l<=mid)add(l,r,x,L[p],tl,mid); if(r>mid)add(l,r,x,R[p],mid+1,tr); } }tr; int main(){ scanf("%d",&T); while(T--){ scanf("%s",s+1);n=strlen(s+1); memset(nr,0,sizeof nr),memset(rnk,0,sizeof rnk); for(int i=1;i<=n;i++)rnk[i]=s[i],sa[i]=i; for(int i=1;i<=n;i<<=1){ sort(sa+1,sa+1+n,[&](int a,int b){ return rnk[a]<rnk[b]||rnk[a]==rnk[b]&&rnk[a+i]<rnk[b+i]; }); for(int j=1,t=0;j<=n;nr[sa[j++]]=t) t+=rnk[sa[j]]!=rnk[sa[j-1]]||rnk[sa[j]+i]!=rnk[sa[j-1]+i]; memcpy(rnk,nr,sizeof nr); } for(int i=1,k=0;i<=n;i++){ if(k)k--; for(;s[i+k]==s[sa[rnk[i]-1]+k];k++); *st[rnk[i]]=k; } memset(hd,0,sizeof hd); memset(w,0,sizeof w); memset(ind,0,sizeof ind); tot=m=0;u.clear(); memset(&tr,0,sizeof tr); for(int i=1;(1<<i)<=n;i++) for(int j=0;j+(1<<i)<=n+1;j++) st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]); scanf("%d",&na); for(int i=1;i<=n;i++)v[i].clear(); for(int i=1;i<=na;i++) scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i; sort(a+1,a+1+na); for(int i=1;i<=na;i++) ps[a[i].id]=i,tr.ins(i,rt[i-1]),rt[i]=tot; for(int i=1;i<=na;i++)w[p[i]=++tot]=a[i].r-a[i].l+1; for(stt&s:u)adde(s.l,p[s.r]); scanf("%d",&nb); for(int i=1;i<=nb;i++)scanf("%d%d",l+i,r+i); int x;scanf("%d",&x); for(int i=1;i<=x;i++){ int x,y;scanf("%d%d",&x,&y); v[ps[x]].push_back(y); } for(int i=1;i<=na;i++)for(int j:v[i]){ int x=qry1(rnk[l[j]],r[j]-l[j]+1),y=qry2(rnk[l[j]]+1,r[j]-l[j]+1); tr.add(x,y,p[i],rt[upper_bound(a+1,a+1+na,stt{l[j],r[j]})-a-1]); } memset(dis,0,sizeof dis); int l=1,r=0; for(int i=1;i<=tot;i++)if(!ind[i])que[++r]=i,dis[i]=w[i]; while(l<=r){ int p=que[l++]; for(int x=hd[p];x;x=e[x].nxt){ dis[e[x].v]=max(dis[e[x].v],dis[p]+w[e[x].v]); if(!--ind[e[x].v])que[++r]=e[x].v; } } if(r!=tot+1)puts("-1");else printf("%lld\n",*max_element(dis+1,dis+1+tot)); } return 0; } ```