zoukankan      html  css  js  c++  java
  • 【UOJ131/NOI2015D2T2-品酒大会】sam求后缀树

    题目链接:http://uoj.ac/problem/131

    题意:给出一个字符串,第i个字符对应的值为a[i], 对于i∈[0,n),求最长公共前缀大于等于i的字串对个数,并求这些字符串对开头对应值相乘最大值。n=3*10^5

     

    题解:

    学了个厉害的东西啊。。。

    正解好像是sa+并查集(合并height)

    然而我学了个用sam的做法。。

     

    对于第一问:

    首先我们要知道,建立后缀自动机之后,parent树就是逆序串的后缀树。

    why?看这个博客好了:http://z55250825.blog.163.com/blog/static/15023080920144542541495/

     

    直接逆序建后缀自动机,

    因为对于现在parent树而言,任意两点的LCP等于两点在树上的LCA的step(step就是sam里的那个step。。一开始没想清楚还以为是parent-tree上的深度。。于是WA了。。)

    这是转化成一个简单的树形dp了:按逆拓扑序更新(从孩子到parent),对于当前点x,看它是多少对点对的lcp。

    假设有四个孩子,孩子的点数(就是这棵子树上有多少个点)分别为s1,s2,s3,s4

    cnt[x]=1*(s1+s2+s3+s4)(这是x到x的孩子)  +  (s1+s2+s3)*s4  + (s1+s2)*s3  + s1*s2

    那我们每遍历一个孩子y,就sum[x]+=sum[y],对于一个新的孩子yy,cnt[x]+=sum[x]*sum[yy];

     

     

    对于第二问:

    对于当前的parent树而言,等价于求parent树上两个叶节点乘积的最大值。

    又因为考虑到ai可能是负数,所以我们只需要记录最大值,次大值,最小值,次小值就可以了。

     

    参考题解:http://www.cnblogs.com/joyouth/p/5366396.html

    注意很多细节。。

    sam真的超厉害。。可以直接转化成后缀树和后缀数组。。

    ORZ。。

     

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<queue>
    using namespace std;
    
    typedef long long LL;
    const int N=2*3*100010;
    const LL INF=1LL<<62;
    int sl,cl,tot,last,c[N],in[N],first[N],step[N],pre[N],son[N][30];
    LL w[N],cnt[N],ans[N],mx[N],smx[N],mn[N],smn[N],sum[N];
    char s[N];
    bool vis[N];
    queue<int> Q;
    
    LL maxx(LL x,LL y){return x>y ? x:y;}
    LL minn(LL x,LL y){return x<y ? x:y;}
    void gmax(LL &x,LL y){x=maxx(x,y);}
    void gmin(LL &x,LL y){x=minn(x,y);}
    
    int add_node(int x)
    {
        step[++tot]=x;
        return tot;
    }
    
    void clear()
    {
        memset(son,0,sizeof(son));
        memset(pre,0,sizeof(pre));
        memset(step,0,sizeof(step));
        memset(in,0,sizeof(in));
        // memset(cnt,0,sizeof(cnt));
        memset(sum,0,sizeof(sum));
        tot=0;add_node(0);last=1;
    }
    
    void extend(int ch)
    {
        int p=last,np=add_node(step[p]+1);
        while(p && !son[p][ch])
        {
            son[p][ch]=np;
            in[np]++;
            p=pre[p];
        }
        if(!p) pre[np]=1;
        else
        {
            int q=son[p][ch];
            if(step[q]==step[p]+1) pre[np]=q;
            else
            {
                int nq=add_node(step[p]+1);
                for(int i=1;i<=26;i++) 
                    if(son[q][i]) son[nq][i]=son[q][i],in[son[q][i]]++;
                pre[nq]=pre[q];
                pre[np]=pre[q]=nq;
                while(p && son[p][ch]==q) in[q]--,in[nq]++,son[p][ch]=nq,p=pre[p];
            }
        }
        last=np;
    }
    
    void get_tp()
    {
        while(!Q.empty()) Q.pop();
        memset(vis,0,sizeof(vis));
        Q.push(1);vis[1]=1;cl=0;
        while(!Q.empty()) 
        {
            int x=Q.front();c[++cl]=x;vis[x]=0;Q.pop();
            for(int i=1;i<=26;i++)
            {
                int y=son[x][i];
                if(!y) continue;
                in[y]--;
                if(!in[y] && !vis[y]) vis[y]=1,Q.push(y);
            }
        }
    }
    
    int main()
    {
        freopen("a.in","r",stdin);
        int x,y,ch;
        scanf("%d",&sl);
        scanf("%s",s+1);
        for(int i=1;i<=sl;i++) scanf("%lld",&w[i]);
        
        clear();
        for(int i=sl;i>=1;i--) extend(s[i]-'a'+1);
        get_tp();
        
        for(int i=1;i<=tot;i++) mx[i]=-INF,smx[i]=-INF,mn[i]=INF,smn[i]=INF;
        x=1;
        for(int i=sl;i>=1;i--)
        {
            ch=s[i]-'a'+1;
            x=son[x][ch];
            mx[x]=mn[x]=w[i];
            sum[x]++;
        }
        
        LL tmp;
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<=sl;i++) ans[i]=-INF;
        for(int i=cl;i>=1;i--)
        {
            y=c[i],x=pre[y];
            tmp=-INF;
            if(smx[y]>-INF) gmax(tmp,mx[y]*smx[y]);
            if(smn[y]<INF)  gmax(tmp,mn[y]*smn[y]);
            gmax(ans[step[y]],tmp);
            cnt[step[x]]+=sum[x]*sum[y];
            sum[x]+=sum[y];
            
            if(mx[y]>=mx[x]) smx[x]=mx[x],mx[x]=mx[y];//debug >=
            else if(mx[y]>smx[x]) smx[x]=mx[y];
            if(mn[y]<=mn[x]) smn[x]=mn[x],mn[x]=mn[y];//debug <=
            else if(mn[y]<smn[x]) smn[x]=mn[y];
        }
        // for(int i=0;i<sl;i++) printf("x = %d  cnt = %lld  ans = %lld
    ",i,cnt[i],ans[i]);
        for(int i=sl-1;i>=0;i--) 
        {
            
            cnt[i]+=cnt[i+1];
            gmax(ans[i],ans[i+1]);
        }
        for(int i=0;i<sl;i++) 
        {
            if(!cnt[i]) ans[i]=0;
            printf("%lld %lld
    ",cnt[i],ans[i]);
        }
        return 0;
    }

     

  • 相关阅读:
    CCoolBar 的替代方案 CDockablePane。
    CTreeView 的教程
    散列表
    Add and Search Word
    Contains Duplicate II
    Word Search II
    Implement Trie (Prefix Tree)
    (转)多进程 & 多线程的区别与适用场景
    (转)进程控制:进程的创建、终止、阻塞、唤醒和切换
    (转)几个常用的操作系统进程调度算法
  • 原文地址:https://www.cnblogs.com/KonjakJuruo/p/5929170.html
Copyright © 2011-2022 走看看