洛谷 P5284 [十二省联考2019]字符串问题 后缀数组+主席树优化加边

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/89385542

题目:
https://www.luogu.org/problemnew/show/P5284

分析:
考虑怎样构造一个合法串。我们从每一个 A A 类串向他支配的 B B 类串连边, B B 类串向以他为前缀的 A A 类串连边,形成一个有向图。每一个 A A 类串权值设为他的 l e n len B B 类串设为0。那么一条路径的权值和就是某个合法串的长度。
显然相当于求图最长链,存在环则解无限大,不然可以拓扑排序求。

考虑 B B 类串连向 A A 类串的边。显然要满足 A B |A|≥|B| l c p ( A , B ) = B lcp(|A|,|B|)=|B|
考虑以 l [ A ] l[A] l [ B ] l[B] 开头的后缀,显然他们的 l c p B lcp≥|B|
对原串先跑出后缀数组。
我们先把所有串按长度从大到小排序,可以维护一棵可持久化线段树维护长度在 [ 1 , x ] [1,x] A A 类串,线段下标为该串的 r a n k rank 。对于串 B B 来说, l c p B lcp≥|B| 的在rank上是一个单峰函数,二分求得左右边界。相当于 r a n k rank [ L , R ] [L,R] 内的比 B B 长的串都是可以连边的,向对应线段树节点连边。因为要不断插入更短的串,所以前后线段树不一样,要可持久化。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#define LL long long

const int maxn=2e5+7;

using namespace std;

int T,na,nb,m,len,tot,cnt,root,x,y;
int ls[maxn*30],val[maxn*30],r[maxn*30];
LL f[maxn*30],ans;
char s[maxn];

queue <int> q;

struct rec{
    int l,r,num;
}a[maxn],b[maxn];

struct node{
    int l,r;
}t[maxn*30];

struct edge{
    int y,next;
}g[maxn*60];

struct suffle_array{
    int x[maxn],y[maxn],c[maxn],lg[maxn],rank[maxn],sa[maxn],h[maxn][20];
    void getsa()
    {
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        memset(rank,0,sizeof(rank));
        memset(sa,0,sizeof(sa));
        memset(h,0,sizeof(h));
        int n=len,m=1000;
        for (int i=1;i<=n;i++) lg[i]=trunc(log(i+0.5)/log(2));
        for (int i=1;i<=m;i++) c[i]=0;
        for (int i=1;i<=n;i++) x[i]=s[i];
        for (int i=1;i<=n;i++) c[x[i]]++;
        for (int i=1;i<=m;i++) c[i]+=c[i-1];
        for (int i=n;i>0;i--) sa[c[x[i]]--]=i;
        for (int k=1;k<=n;k<<=1)
        {
        	int num=0;
        	for (int i=n-k+1;i<=n;i++) y[++num]=i;
        	for (int i=1;i<=n;i++) if (sa[i]>k) y[++num]=sa[i]-k;
        	for (int i=1;i<=m;i++) c[i]=0;
        	for (int i=1;i<=n;i++) c[x[i]]++;
        	for (int i=1;i<=m;i++) c[i]+=c[i-1];
        	for (int i=n;i>0;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        	swap(x,y);
        	num=1;
        	x[sa[1]]=1;
        	for (int i=2;i<=n;i++)
        	{
        		if ((y[sa[i]]!=y[sa[i-1]]) || (y[sa[i]+k]!=y[sa[i-1]+k]))
        		{
        			x[sa[i]]=++num;
        		}
        		else x[sa[i]]=num;
        	}
        	if (num>=n) break;
        	m=num;
        }
        for (int i=1;i<=n;i++) rank[i]=x[i];
    }
    void getheight()
    {
        int n=len,k=0;
        for (int i=1;i<=n;i++)
        {
            if (k) k--;
            int j=sa[rank[i]-1];
            while ((i+k<=n) && (j+k<=n) && (s[i+k]==s[j+k])) k++;
            h[rank[i]][0]=k;
        }
        int c=1;
        for (int j=1;j<20;j++)
        {
            for (int i=1;i+2*c-1<=n;i++) h[i][j]=min(h[i][j-1],h[i+c][j-1]);
            c<<=1;
        }
    }
    int lcp(int x,int y)
    {
        x=rank[x],y=rank[y];
        if (x>y) swap(x,y);
        x++;
        int c=lg[y-x+1];
        return min(h[x][c],h[y-(1<<c)+1][c]);
    }
    int getleft(int x,int k)
    {
        int l=1,r=x-1,ans=x;
        while (l<=r)
        {
            int mid=(l+r)/2;
            if (lcp(sa[mid],sa[x])>=k) r=mid-1,ans=mid;
                                  else l=mid+1;
        }
        return ans;
    }
    int getright(int x,int k)
    {
        int l=x+1,r=len,ans=x;
        while (l<=r)
        {
            int mid=(l+r)/2;
            if (lcp(sa[mid],sa[x])>=k) l=mid+1,ans=mid;
                                  else r=mid-1;
        }
        return ans;
    }
}sa;

bool cmp(rec a,rec b)
{
    return a.r-a.l>b.r-b.l;
}

void add(int x,int y)
{
    g[++tot]=(edge){y,ls[x]};
    ls[x]=tot;
}

void ins(int &p,int q,int l,int r,int x,int num)
{
    p=++cnt;
    if (l==r)
    {
        if (q) add(p+na+nb,q+na+nb);
        add(p+na+nb,num);
        return;
    }
    int mid=(l+r)/2;
    if (x<=mid) t[p].r=t[q].r,ins(t[p].l,t[q].l,l,mid,x,num);
           else t[p].l=t[q].l,ins(t[p].r,t[q].r,mid+1,r,x,num);
    if (t[p].l) add(p+na+nb,t[p].l+na+nb);
    if (t[p].r) add(p+na+nb,t[p].r+na+nb);
}

void solve(int p,int l,int r,int x,int y,int num)
{
    if (!p) return;
    if ((l==x) && (r==y))
    {
        add(num,p+na+nb);
        return;
    }
    int mid=(l+r)/2;
    if (y<=mid) solve(t[p].l,l,mid,x,y,num);
    else if (x>mid) solve(t[p].r,mid+1,r,x,y,num);
    else
    {
        solve(t[p].l,l,mid,x,mid,num);
        solve(t[p].r,mid+1,r,mid+1,y,num);
    }
}

void gettop()
{
    int n=na+nb+cnt,m=tot;
    for (int i=1;i<=n;i++) r[i]=0;
    for (int i=1;i<=m;i++) r[g[i].y]++;
    int num=0;
    for (int i=1;i<=n;i++)
    {
        if (!r[i])
        {
            f[i]=(LL)val[i];
            q.push(i);
            num++;
        }
    }
    ans=0;
    memset(f,0,sizeof(f));
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        ans=max(ans,f[x]);
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if (r[y])
            {
                f[y]=max(f[y],f[x]+(LL)val[y]);
                r[y]--;
                if (!r[y]) q.push(y),num++;
            }
        }
    }
    if (num==n) printf("%lld\n",ans);
           else printf("-1\n");
}

int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        sa.getsa();
        sa.getheight();
        memset(val,0,sizeof(val));
        scanf("%d",&na);
        for (int i=1;i<=na;i++)
        {
            scanf("%d%d",&a[i].l,&a[i].r);
            a[i].num=i;
            val[i]=a[i].r-a[i].l+1;
        }
        scanf("%d",&nb);
        for (int i=1;i<=nb;i++)
        {
            scanf("%d%d",&b[i].l,&b[i].r);
            b[i].num=i;
        }
        sort(a+1,a+na+1,cmp);
        sort(b+1,b+nb+1,cmp);		
        scanf("%d",&m);
        memset(ls,0,sizeof(ls));
        tot=0;
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y+na);
        }		
        cnt=root=0;			
        for (int i=1,j=1;j<=nb;j++)
        {
            while ((a[i].r-a[i].l>=b[j].r-b[j].l) && (i<=na))
            {
                ins(root,root,1,len,sa.rank[a[i].l],a[i].num);
                i++;
            }
            int L=sa.getleft(sa.rank[b[j].l],b[j].r-b[j].l+1);
            int R=sa.getright(sa.rank[b[j].l],b[j].r-b[j].l+1);
            solve(root,1,len,L,R,b[j].num+na);
        }
        gettop();
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/89385542
今日推荐