1. 程式人生 > >BZOJ.4199.[NOI2015]品酒大會(字尾自動機 樹形DP)

BZOJ.4199.[NOI2015]品酒大會(字尾自動機 樹形DP)

BZOJ
洛谷

字尾陣列做法
洛谷上SAM比SA慢...BZOJ SAM卻能快近一倍...

只考慮求極長相同子串,即所有後綴之間的LCP。
而後綴的LCP在後綴樹的LCA處。同差異這道題,在每個點處樹形DP統計它作為LCA時的貢獻即可(有多少對字尾以它為LCA)。
而第二問,同樣維護子樹內的最大值次大值、最小值次小值作為答案即可。

非字尾節點的\(size=0\),最值的初值同樣要設成\(INF\)...但是最後也要一樣DP。
初始設成\(INF\)在最後轉移的時候同樣要判...(不能是兩個\(INF\)相乘)不妨直接只在\(size\geq2\)的時候DFS/更新答案。

//95648kb   2744ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=3e5+5,INF=1<<30;

//char OUT[8000000],*O=OUT;//7e6 isn't enough...
inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}
//inline void print(LL x)
//{
//  static char obuf[20];
//  if(x<0) x=-x, *O++='-';
//  if(x)
//  {
//      int t=0; while(x) obuf[++t]=x%10+48, x/=10;
//      while(t) *O++=obuf[t--];
//  }
//  else *O++='0';
//}

struct Suffix_Automaton
{
    #define S N<<1
    int las,tot,son[S][26],len[S],fa[S],tm[S],A[S],sz[S],mx1[S],mx2[S],mn1[S],mn2[S];
    LL sum[N],Ans[N];
    char s[N];
    #undef S
    void Insert(int c,int v)
    {
        int p=las,np=++tot;
        len[las=np]=len[p]+1, sz[np]=1;
        mx1[np]=mn1[np]=v, mx2[np]=-INF, mn2[np]=INF;
        for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
        if(!p) fa[np]=1;
        else
        {
            int q=son[p][c];
            if(len[q]==len[p]+1) fa[np]=q;
            else
            {
                int nq=++tot; len[nq]=len[p]+1;
                mx1[nq]=mx2[nq]=-INF, mn1[nq]=mn2[nq]=INF;
                memcpy(son[nq],son[q],sizeof son[q]);
                fa[nq]=fa[q], fa[q]=fa[np]=nq;
                for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
            }
        }
    }
    void Solve(const int n)
    {
        las=tot=1, scanf("%s",s+1);
        for(int i=1; i<=n; ++i) A[i]=read();
        for(int i=n; i; --i) Insert(s[i]-'a',A[i]);

        for(int i=1; i<=tot; ++i) ++tm[len[i]];
        for(int i=1; i<=n; ++i) tm[i]+=tm[i-1];
        for(int i=tot; i; --i) A[tm[len[i]]--]=i;

        for(int i=tot,v=A[i],x,l; i; v=A[--i])
        {
            x=fa[v], l=len[x];
            sum[l]+=1ll*sz[x]*sz[v], sz[x]+=sz[v];
            mx1[v]>mx1[x]?(mx2[x]=mx1[x],mx1[x]=mx1[v]):(mx2[x]=std::max(mx2[x],mx1[v]));
            mx2[x]=std::max(mx2[x],mx2[v]);
            mn1[v]<mn1[x]?(mn2[x]=mn1[x],mn1[x]=mn1[v]):(mn2[x]=std::min(mn2[x],mn1[v]));
            mn2[x]=std::min(mn2[x],mn2[v]);
        }
        memset(Ans,-0x3f,sizeof Ans);
        for(int x=1; x<=tot; ++x)
            sz[x]>=2 && (Ans[len[x]]=std::max(Ans[len[x]],std::max(1ll*mx1[x]*mx2[x],1ll*mn1[x]*mn2[x])));

        for(int i=n-1; ~i; --i) sum[i]+=sum[i+1], Ans[i]=std::max(Ans[i],Ans[i+1]);
        for(int i=0; i<n; ++i) printf("%lld %lld\n",sum[i],sum[i]?Ans[i]:0);
//      for(int i=0; i<n; ++i) print(sum[i]),*O++=' ',print(sum[i]?Ans[i]:0),*O++='\n';
//      fwrite(OUT,1,O-OUT,stdout);
    }
}sam;

int main()
{
    sam.Solve(read());
    return 0;
}