• 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
    ");
                    continue;
                }else
                {
                    printf("%d
    ",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
    ");
                continue;
            }
            int ret=0;
            for(int i=1;i<=na;i++)
            {
                ret=max(ret,spfa(i));
            }
            printf("%d
    ",ret);
        }
    }

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

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

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

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

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

    怎么定位?

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

    具体地,对于每次给出的一组$[l,r]$,由于我们是对原串反串建的后缀自动机,显然$l$对应的节点在后缀树上深度更深,我们从这个点向上倍增,倍增到深度最浅且满足$lengeq 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
    ",spfa());
        }
        return 0;
    }
  • 相关阅读:
    轻松实现内容的无缝平滑滚动
    世界“新七大奇迹”揭晓 中国长城成功入选
    Blog_Backup:绿色博客备份工具
    【Vista中系统准备工具存在隐患,自动清除用户资料】
    Oracle大连全球支持中心运营就在我们公司前面!
    ASP.NET2.0下使用AJAX调用Webservice的方法
    配合AJAX天气预报的WebService
    Google兑现承诺你可以从google上删除你的搜索记录了
    全体注意:@live.com 形势不妙,可能三周内被收回
    国内软件联盟抨击微软以非正规手段推文档标准
  • 原文地址:https://www.cnblogs.com/zhangleo/p/11130287.html
Copyright © 2020-2023  润新知