bzoj4199 [Noi2015]品酒大會(SAM+線段樹)

這回換了個SAM寫,SA的做法見:portal

一開始以爲可以做到線性…結果寫了之後發現還是要掛棵線段樹…
我們考慮對反串建SAM,搞出後綴樹,這樣任意兩個後綴的lca就是他們的lcp。於是我們可以在每個lca處統計答案。
不過一個點可能會覆蓋一段長度區間,所以要區間修改答案。
複雜度O(26n+nlogn)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 1e18
#define N 300010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n=0,m=0,a[N],par[N<<1],mx[N<<1],sz[N<<1],mx1[N<<1],mx2[N<<1],mn1[N<<1],mn2[N<<1],son[N<<1][26],rt=0,lst=0,cnt[N],q[N<<1];
char s[N];ll ans1[N],ans2[N];
struct node{
    ll ans1,ans2;
}tr[N<<2];
inline void get_S(){
    char ch=gc();
    while(ch<'a'||ch>'z') ch=gc();
    while(ch>='a'&&ch<='z') s[++m]=ch,ch=gc();
}
inline int newp(){
    ++n;mx1[n]=mx2[n]=-1;mn1[n]=mn2[n]=1;return n;
}
inline void extend(int ch,int val){
    int p=lst,np=newp();mx[np]=mx[p]+1;lst=np;sz[np]=1;if(val>=0) mx1[np]=val;else mn1[np]=val;
    for(;p&&!son[p][ch];p=par[p]) son[p][ch]=np;
    if(!p){par[np]=rt;return;}
    int q=son[p][ch];
    if(mx[q]==mx[p]+1){par[np]=q;return;}
    int nq=newp();mx[nq]=mx[p]+1;memcpy(son[nq],son[q],sizeof(son[q]));
    par[nq]=par[q];par[q]=par[np]=nq;
    for(;p&&son[p][ch]==q;p=par[p]) son[p][ch]=nq;
}
inline void upd(int p,int val){
    if(val>mx1[p]) mx2[p]=mx1[p],mx1[p]=val;
    else if(val>mx2[p]) mx2[p]=val;
}
inline void upd1(int p,int val){
    if(val<mn1[p]) mn2[p]=mn1[p],mn1[p]=val;
    else if(val<mn2[p]) mn2[p]=val;
}
inline void change(int p,int l,int r,int x,int y,ll val1,ll val2){
    if(x<=l&&r<=y){tr[p].ans1+=val1;tr[p].ans2=max(tr[p].ans2,val2);return;}
    int mid=l+r>>1;
    if(x<=mid) change(p<<1,l,mid,x,y,val1,val2);
    if(y>mid) change(p<<1|1,mid+1,r,x,y,val1,val2);
}
inline void dfs(int p,int l,int r){
    if(l==r){ans1[l]=tr[p].ans1;ans2[l]=ans1[l]?tr[p].ans2:0;return;}
    int mid=l+r>>1;tr[p<<1].ans1+=tr[p].ans1;tr[p<<1].ans2=max(tr[p<<1].ans2,tr[p].ans2);
    tr[p<<1|1].ans1+=tr[p].ans1;tr[p<<1|1].ans2=max(tr[p<<1|1].ans2,tr[p].ans2);
    dfs(p<<1,l,mid);dfs(p<<1|1,mid+1,r);
}
inline void build(int p,int l,int r){
    tr[p].ans2=-inf;if(l==r) return;int mid=l+r>>1;
    build(p<<1,l,mid);build(p<<1|1,mid+1,r);
}
int main(){
//  freopen("savour14.in","r",stdin);
//  freopen("a.out","w",stdout);
    read();get_S();for(int i=1;i<=m;++i) a[i]=read();rt=lst=newp();
    for(int i=m;i>=1;--i) extend(s[i]-'a',a[i]);
    for(int i=1;i<=n;++i) cnt[mx[i]]++;
    for(int i=1;i<=m;++i) cnt[i]+=cnt[i-1];
    for(int i=n;i>=1;--i) q[cnt[mx[i]]--]=i;mx[0]=-1;build(1,1,m);
    for(int i=n;i>=1;--i){
        int x=q[i];sz[par[x]]+=sz[x];
        upd(par[x],mx1[x]);upd(par[x],mx2[x]);upd1(par[x],mn1[x]);upd1(par[x],mn2[x]);
        if(sz[x]==1) continue;ll res;if(mx2[x]==-1&&mn2[x]==1) res=(ll)mx1[x]*mn1[x];
        else res=max(mx2[x]==-1?0:(ll)mx1[x]*mx2[x],mn2[x]==1?0:(ll)mn1[x]*mn2[x]);
        change(1,1,m,mx[par[x]]+1+1,mx[x]+1,(ll)sz[x]*(sz[x]-1)>>1,res);
    }dfs(1,1,m);
    for(int i=1;i<=m;++i) printf("%lld %lld\n",ans1[i],ans2[i]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章