bzoj 5496

Posted zhangleo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj 5496相关的知识,希望对你有一定的参考价值。

多有趣的一道题啊...

考场上的思路:

首先我们可以通过hash判断出每个$B$类串是几个$A$类串的前缀,从这个$B$类串向对应的$A$类串连边

然后我们直接按支配关系从$A$类串向$B$类串连边,相当于以$B$类串为中转构造了一张$A$类串的图,在这张图上跑一次最长路即可

这样做是40分(所以40分不需要后缀数组!不需要后缀自动机!)

40分代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ull unsigned long long
#define seed 131131
using namespace std;
char s[200005];
int l[200005];
int r[200005];
ull p[200005];
ull has[200005];
bool vis[200005];
int T;
struct Edge

    int nxt;
    int to;
edge[1000005];
int head[10005];
int cnt=1;
int na,nb,m;
bool used[10005];
int inr[10005];
int dis[10005];
void init()

    memset(inr,0,sizeof(inr));
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    cnt=1;

void adde(int l,int r)

    edge[cnt].nxt=head[l];
    edge[cnt].to=r;
    head[l]=cnt++;

int spfa(int x)

      memset(used,0,sizeof(used));
    memset(dis,0,sizeof(dis));
    queue <int> M;
    M.push(x);
    used[x]=1;
    dis[x]=r[x]-l[x]+1;
    int ans=dis[x];
    while(!M.empty())
    
        int u=M.front();
        M.pop();
        for(int i=head[u];i!=-1;i=edge[i].nxt)
        
            int to=edge[i].to;
            if(to>na)
            
                continue;
            
            if(dis[to]<dis[u]+r[to]-l[to]+1)
            
                dis[to]=dis[u]+r[to]-l[to]+1;
                ans=max(ans,dis[to]);
                if(!used[to])
                
                    used[to]=1;
                    M.push(to);
                
            
        
        used[u]=0;
    
    return ans;

bool tsort()

    queue <int> M;
    int tot=0;
    for(int i=1;i<=na;i++)
    
        if(!inr[i])
        
            M.push(i);
            tot++;
            
    
    while(!M.empty())
    
        int u=M.front();
        M.pop();
        for(int i=head[u];i!=-1;i=edge[i].nxt)
        
            int to=edge[i].to;
            if(to>na)
            
                continue;
            
            inr[to]--;
            if(inr[to]==0)
            
                M.push(to);
                tot++;
            
        
    
    if(tot==na)
    
        return 1;
    
    return 0;

int main()

//    freopen("string.in","r",stdin);
//    freopen("string.out","w",stdout);
    scanf("%d",&T);
    while(T--)
    
        init();
        scanf("%s",s+1);
        int len=strlen(s+1);
        has[0]=0;
        p[0]=1;
        for(int i=1;i<=len;i++)
        
            has[i]=has[i-1]*seed+s[i]-a+1;
            p[i]=p[i-1]*seed;
        
        scanf("%d",&na);
        for(int i=1;i<=na;i++)
        
            scanf("%d%d",&l[i],&r[i]);
        
        scanf("%d",&nb);
        for(int i=1;i<=nb;i++)
        
            int x,y;
            scanf("%d%d",&x,&y);
            ull hh=has[y]-has[x-1]*p[y-x+1];
            for(int j=1;j<=na;j++)
            
                if(r[j]-l[j]+1>=y-x+1)
                
                    if(has[l[j]+y-x]-has[l[j]-1]*p[y-x+1]==hh)
                    
                        if(na!=1)
                        
                            adde(i+na,j);
                        else
                        
                            vis[i]=1;
                        
                    
                
            
        
        scanf("%d",&m);
        bool flag=0;
        for(int i=1;i<=m;i++)
        
            int x,y;
            scanf("%d%d",&x,&y);
            if(na!=1)
            
                y+=na;
                adde(x,y);
            else
            
                if(vis[y])
                
                    flag=1;
                
            
                
        
        if(na==1)
        
            if(flag)
            
                printf("-1\n");
                continue;
            else
            
                printf("%d\n",r[1]-l[1]+1);
                continue;
            
        
        for(int i=1;i<=na;i++)
        
            for(int j=head[i];j!=-1;j=edge[j].nxt)
            
                int to=edge[j].to;
                for(int t=head[to];t!=-1;t=edge[t].nxt)
                
                    int too=edge[t].to;
                    adde(i,too);
                    inr[too]++;
                
            
        
        if(!tsort())
        
            printf("-1\n");
            continue;
        
        int ret=0;
        for(int i=1;i<=na;i++)
        
            ret=max(ret,spfa(i));
        
        printf("%d\n",ret);
    

然而,这个算法很难再优化了,因为爆枚hash就已经超时了,我们需要更搞笑高效的算法。

接下来的内容需要后缀自动机与后缀树有关知识

首先有个性质:原串的parent树是反串的后缀树!

一个字符串的子串一定是一个后缀的前缀!

据此,我们可以直接建起反串的parent树(实际也就是原串的后缀树),然后在后缀树上定位出所有$A$,$B$类串,这样前缀的问题就迎刃而解了,因为后缀树上的祖宗节点一定是子代节点的前缀,同时也相当于优化了建图,因为这样我们只需在后缀树上对父子进行连边即可,避免了大量的建边操作

怎么定位?

在建立后缀自动机时,我们维护原串中每个位置在后缀自动机中所对应的节点编号,然后在后缀树上倍增即可

具体地,对于每次给出的一组$[l,r]$,由于我们是对原串反串建的后缀自动机,显然$l$对应的节点在后缀树上深度更深,我们从这个点向上倍增,倍增到深度最浅且满足$len>r-l+1$的点即可,此时这个点就是这个子串在后缀树上定位到的点!

然后我们记录一个节点被不同的串定位到的次数即可

同时考虑另一个问题:由于这样建起的后缀树有大量压缩,因此对于两个不同的串,可能会被定位到后缀树上的同一个点,这样显然是不对的

因此我们考虑展开压缩:在后缀树上如果一个点被不同的串定位了,那么我们把所有这些串按照长度排序后对对每种长度分别建一个点,保证长度从小到大新建的点深度由浅到深,同时不断由父节点向子节点连边,边权为0建图即可

最后支配关系直接找到两个串在后缀树上定位到的节点后连边,边权为$A$类串长度

然后跑拓扑最长路即可

(据说可以后缀数组+st表+主席树优化建图搞,不过窝不会...)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#define ll long long
using namespace std;
struct Edge

    int nxt;
    int to;
    int val;
edge[800005];
struct node

    int po,ilen;
    node ()
    node (int a,int b):po(a),ilen(b)
    friend bool operator < (node a,node b)
    
        return a.po==b.po?a.ilen<b.ilen:a.po<b.po;
    
;
set <node> S;
int head[800005];
int cnt=1;
int tranc[400005][27];
int vis[400005];
int l[400005],r[400005],pos[400005];
int va[800005];
int pre[400005];
bool used[800005];
int tf[400005];//字符串中某个位置向后缀自动机中节点的映射
int len[400005];
int inr[800005];
char ch[400005];
ll dis[800005];
int f[400005][25];
vector <int> v[400005];
vector <node> rS[400005];
int tot,las,temptot;
int T,n;
int na,nb,m;
void init()

    for(int i=1;i<=tot;i++)head[i]=va[i]=used[i]=inr[i]=0,dis[i]=-0x3f3f3f3f;
    for(int i=1;i<=temptot;i++)rS[i].clear(),v[i].clear(),vis[i]=pre[i]=len[i]=0,memset(tranc[i],0,sizeof(tranc[i]));
    S.clear();
    tot=las=cnt=1;

void add(int l,int r,int w)

    edge[cnt].nxt=head[l];
    edge[cnt].to=r;
    edge[cnt].val=w;
    head[l]=cnt++;

void ins(int c,int o)

    int nwp=++tot;
    len[nwp]=len[las]+1;
    tf[o]=nwp;
    int lsp;
    for(lsp=las;lsp&&!tranc[lsp][c];lsp=pre[lsp])tranc[lsp][c]=nwp;
    if(!lsp)pre[nwp]=1;
    else 
    
        int lsq=tranc[lsp][c];
        if(len[lsq]==len[lsp]+1)pre[nwp]=lsq;
        else
        
            int nwq=++tot;
            memcpy(tranc[nwq],tranc[lsq],sizeof(tranc[lsq]));
            pre[nwq]=pre[lsq];
            pre[lsq]=pre[nwp]=nwq;
            len[nwq]=len[lsp]+1;
            while(lsp&&tranc[lsp][c]==lsq)tranc[lsp][c]=nwq,lsp=pre[lsp];
        
    
    las=nwp;

void buildtree()

    for(int i=2;i<=tot;i++)v[pre[i]].push_back(i),f[i][0]=pre[i];
    f[1][0]=1;

int Jump(int st,int di)

    for(int i=20;i>=0;i--)if(len[f[st][i]]>=di&&f[st][i]!=1)st=f[st][i];
    if(S.find(node(st,di))==S.end())S.insert(node(st,di)),vis[st]++;
    return st;

void rebuild(int x,int fx)

    if(fx)add(fx,x,0),inr[x]++;
    if(x==1)for(int i=0;i<v[x].size();i++)rebuild(v[x][i],x);return;
    sort(rS[x].begin(),rS[x].end());
    int now=0;
    for(int i=1;i<rS[x].size();i++)if(rS[x][i].po!=rS[x][i-1].po)now=i;break;
    int las=x;
    while(vis[x]>1)
    
        tot++,vis[x]--;
        pos[rS[x][now].ilen]=tot;
        for(int i=now+1;i<rS[x].size();i++)
        
            if(rS[x][i].po!=rS[x][i-1].po)now=i;break;
            else pos[rS[x][i].ilen]=tot;
        
        add(las,tot,0),inr[tot]++;
        las=tot;
    
    for(int i=0;i<v[x].size();i++)rebuild(v[x][i],las);

ll spfa()

    queue <int> M;
    M.push(1);
    dis[1]=0;
    int cct=1;
    ll ans=0;
    while(!M.empty())
    
        int u=M.front();
        M.pop();
        ans=max(ans,dis[u]+(ll)va[u]);
        for(int i=head[u];i;i=edge[i].nxt)
        
            int to=edge[i].to;
            dis[to]=max(dis[u]+(ll)edge[i].val,dis[to]);
            inr[to]--;
            if(!inr[to])M.push(to),cct++;
        
    
    if(cct==tot)return ans;
    else return -1;

int main()

    scanf("%d",&T);
    while(T--)
    
        init();
        scanf("%s",ch+1);
        n=strlen(ch+1);
        for(int i=n;i>=1;i--)ins(ch[i]-a+1,i);
        buildtree();
        for(int i=1;i<=20;i++)for(int j=1;j<=tot;j++)f[j][i]=f[f[j][i-1]][i-1];
        scanf("%d",&na);
        for(int i=1;i<=na;i++)
        
            scanf("%d%d",&l[i],&r[i]);
            pos[i]=Jump(tf[l[i]],r[i]-l[i]+1);
        
        scanf("%d",&nb);
        for(int i=na+1;i<=na+nb;i++)
        
            scanf("%d%d",&l[i],&r[i]);
            pos[i]=Jump(tf[l[i]],r[i]-l[i]+1);
        
        for(int i=1;i<=na+nb;i++)rS[pos[i]].push_back(node(r[i]-l[i]+1,i));
        temptot=tot;
        rebuild(1,0);
        for(int i=1;i<=na;i++)va[pos[i]]=r[i]-l[i]+1;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        
            int x,y;
            scanf("%d%d",&x,&y);
            y+=na;
            add(pos[x],pos[y],r[x]-l[x]+1),inr[pos[y]]++;
        
        printf("%lld\n",spfa());
    
    return 0;

 

以上是关于bzoj 5496的主要内容,如果未能解决你的问题,请参考以下文章

HDU 5496 Beauty of Sequence

hzwer的bzoj题单

踩着神犇的脚印走--hzwer刷题表in bzoj

板刷bzoj计划(真香预警)

hdu-5496 Beauty of Sequence(递推)

bzoj2127&bzoj2132