1. 程式人生 > >【[NOI2015]品酒大會】

【[NOI2015]品酒大會】

height 區間和 當前 tin 問題 cstring ems 復雜 基本

可能是最傻的做法了

暴力單調棧+\(st\)

首先看到這道題就基本知道這是個\(SA\)了,先無腦敲上\(SA\)和求\(height\)的板子

之後嘗試搞一下第一問

發現第一問就是求出滿足\(lcp(i,j)>=k\)\((i,j)\)有多少對

我們可以用一個暴力合並的單調棧來做

現在的問題轉化為求出\(height\)數組所有子區間的最小值的和

我們可以考慮一個動態往序列末尾加數的過程

也就是我們往末尾加一個數都會和之前所有的數形成一個新的區間

考慮快速算出這些區間的最小值的和

我們可以對每一個數存儲一個\(a_i\),表示\(i\)到當前序列末尾的最小值是多少

我們每次加入一個數可以對更新一下所有的\(a_i\)

,把所有比當前加入的數大的\(a_i\)變成當前數就好了

這不就\(T\)了嗎

我們發現我們只需要求出所有\(a_i\)的和,並不需要關心這個\(i\)來自哪裏,於是我們可以把相等的\(a_i\)放在一起計算,也就是每次新加入一個數就暴力掃一遍把那些比當前加入數大的合並到一個\(a_i\)

看起來復雜度並不科學,但是最壞情況下就相當於是一個線段樹的復雜度了,\(O(n)\)的,跑的還挺快的

同時還需要維護一個時間戳,在被合並的時候利用時間戳統計一下這個元素的貢獻

之後第二問,我們可以直接強上單調棧

左右兩邊都先用單調棧掃一遍,掃出每個點往左往右能最遠達到哪裏,之後我們用一個\(st\)表維護區間的最大值和最小值,每次只需要從這個點左邊的區間和右邊的區間裏選出最大值和最小值來組合就行了

代碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define re register
#define LL long long
#define maxn 300005
inline LL read()
{
    re char c=getchar();re LL x=0,r=1;
    while(c<‘0‘||c>‘9‘) {if(c==‘-‘) r=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘) x=(x<<3)+(x<<1)+c-48,c=getchar();return r*x;
}
int n,m,top;
int a[maxn],cnt[maxn],T[maxn],L[maxn],R[maxn];
char S[maxn];
int tp[maxn],tax[maxn],sa[maxn],rk[maxn],het[maxn],log_2[maxn],st[maxn];
LL pre[maxn],Ans[maxn];
LL St[2][maxn][20];
inline void qsort()
{
    for(re int i=0;i<=m;i++) tax[i]=0;
    for(re int i=1;i<=n;i++) tax[rk[i]]++;
    for(re int i=1;i<=m;i++) tax[i]+=tax[i-1];
    for(re int i=n;i;--i) sa[tax[rk[tp[i]]]--]=tp[i];
} 
inline LL max(LL a,LL b){return (a>b)?a:b;}
inline LL min(LL a,LL b){return (a<b)?a:b;}
inline LL ask(int o,int l,int r)
{
    int k=log_2[r-l+1];
    if(o) return max(St[1][l][k],St[1][r-(1<<k)+1][k]);
        else return min(St[0][l][k],St[0][r-(1<<k)+1][k]);
}
int main()
{
    n=read();scanf("%s",S+1);
    memset(St[0],0x3f3f3f3f,sizeof(St[0]));
    memset(St[1],-0x3f3f3f3f,sizeof(St[1]));
    memset(Ans,-0x3f3f3f3f,sizeof(Ans));
    m=750;
    for(re int i=1;i<=n;i++) rk[i]=S[i],tp[i]=i;
    qsort();
    for(re int w=1,p=0;p<n;m=p,w<<=1)
    {
        p=0;
        for(re int i=1;i<=w;i++) tp[++p]=n-w+i;
        for(re int i=1;i<=n;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
        qsort();
        for(re int i=1;i<=n;i++) std::swap(rk[i],tp[i]);
        rk[sa[1]]=p=1;
        for(re int i=2;i<=n;i++) rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])?p:++p;
    }
    int k=0;
    for(re int i=1;i<=n;i++)
    {
        if(k) --k;
        int j=sa[rk[i]-1];
        while(S[i+k]==S[j+k]) ++k;
        het[rk[i]]=k;
    }
    for(re int i=2;i<=n;i++)
    {
        int now=1;
        while(top&&a[top]>=het[i])
            now+=cnt[top],pre[a[top]]+=(LL)((LL)i-(LL)T[top])*(LL)cnt[top],top--;
        a[++top]=het[i],T[top]=i,cnt[top]=now;
    }
    while(top) pre[a[top]]+=(LL)((LL)n+1ll-(LL)T[top])*(LL)cnt[top],top--;
    for(re int i=n-1;i>=0;--i) pre[i]+=pre[i+1];
    
    for(re int i=1;i<=n;i++)
    {
        while(top&&het[i]<het[st[top]]) R[st[top]]=i,top--;
        st[++top]=i;
    }
    while(top) R[st[top]]=n+1,top--;
    for(re int i=n;i;--i)
    {
        while(top&&het[i]<het[st[top]]) L[st[top]]=i,top--;
        st[++top]=i;
    }
    while(top) L[st[top]]=1,top--;
    
    for(re int i=1;i<=n;i++) St[0][rk[i]][0]=St[1][rk[i]][0]=read();
    for(re int i=2;i<=n;i++) log_2[i]=1+log_2[i>>1];
    for(re int j=1;j<=19;j++)
        for(re int i=1;i+(1<<j)-1<=n;i++)
            St[0][i][j]=min(St[0][i][j-1],St[0][i+(1<<(j-1))][j-1]),
            St[1][i][j]=max(St[1][i][j-1],St[1][i+(1<<(j-1))][j-1]);
            
    for(re int i=1;i<=n;i++)
    {
        int l=L[i],r=R[i]-1;
        if(l>i-1) continue;
        if(i>r) continue; 
        LL A=ask(1,l,i-1),B=ask(0,l,i-1),C=ask(1,i,r),D=ask(0,i,r);
        if(Ans[het[i]]<A*C) Ans[het[i]]=A*C; if(Ans[het[i]]<B*D) Ans[het[i]]=B*D;
        if(Ans[het[i]]<A*D) Ans[het[i]]=A*D; if(Ans[het[i]]<B*C) Ans[het[i]]=B*C;
    }
    for(re int i=n-1;i>=0;--i) Ans[i]=max(Ans[i],Ans[i+1]);
    for(re int i=0;i<n;i++)
    if(pre[i]) printf("%lld %lld\n",pre[i],Ans[i]);
        else printf("%lld ",pre[i]),putchar(48),putchar(10);
    return 0;
}

【[NOI2015]品酒大會】