[bzoj4199][NOI2015]品酒大會

4199: [Noi2015]品酒大會

Time Limit: 10 Sec Memory Limit: 512 MB
Submit: 418 Solved: 231
[Submit][Status][Discuss]
Description

一年一度的“幻影閣夏日品酒大會”隆重開幕了。大會包含品嚐和趣味挑戰兩個環節,分別向優勝者頒發“首席品酒家”和“首席獵手”兩個獎項,吸引了衆多品酒師參加。

在大會的晚餐上,調酒師Rainbow 調製了 n 杯雞尾酒。這 n 杯雞尾酒排成一行,其中第 i 杯酒 1in 被貼上了一個標籤 si ,每個標籤都是 26 個小寫英文字母之一。設 Str(l,r) 表示第 l 杯酒到第 r 杯酒的 rl+1 個標籤順次連接構成的字符串。若 Str(p,po)=Str(q,qo) ,其中 1ppon1qqonpqpop+1=qoq+1=r ,則稱第 p 杯酒與第 q 杯酒是“r 相似” 的。當然兩杯“r 相似” r>1 的酒同時也是“1 相似”、“2 相似”、……、“(r1) 相似”的。特別地,對於任意的 1p,qnpq ,第p 杯酒和第 q 杯酒都是“0 相似”的。

在品嚐環節上,品酒師 Freda 輕鬆地評定了每一杯酒的美味度,憑藉其專業的水準和經驗成功奪取了“首席品酒家”的稱號,其中第 i 杯酒 1in 的美味度爲 ai 。現在Rainbow 公佈了挑戰環節的問題:本次大會調製的雞尾酒有一個特點,如果把第 p 杯酒與第 q 杯酒調兌在一起,將得到一杯美味度爲 apaq 的酒。現在請各位品酒師分別對於 r=0,1,2,,n1 ,統計出有多少種方法可以選出 2 杯“r 相似”的酒,並回答選擇 2 杯“r 相似”的酒調兌可以得到的美味度的最大值。

Input

輸入文件的第 1 行包含 1 個正整數 n ,表示雞尾酒的杯數。

2 行包含一個長度爲 n 的字符串 S ,其中第 i 個字符表示第 i 杯酒的標籤。

3 行包含 n 個整數,相鄰整數之間用單個空格隔開,其中第 i 個整數表示第 i 杯酒的美味度 ai

Output

輸出文件包括 n 行。第 i 行輸出 2 個整數,中間用單個空格隔開。第 1 個整數表示選出兩杯“(i1) 相似”的酒的方案數,第 2 個整數表示選出兩杯“(i1) 相似”的酒調兌可以得到的最大美味度。若不存在兩杯“(i1) 相似”的酒,這兩個數均爲 0

Sample Input

10
ponoiiipoi
2 1 4 7 4 8 3 6 4 7

Sample Output

45 56
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0

HINT

n<=300000

第一問直接建出後綴數組之後,算出每一個點有貢獻的區間,用height數組從小到大更新答案。
第二問可以用並查集,因爲是從小到大做的,所以產生貢獻的區間會不斷擴大,這樣就可以每次把產生貢獻的區間[l,r] 並起來,並且這一段中的元素最多隻會屬於兩個集合,每次在並查集的過程中保存一下當前集合的最大值和最小值更新答案就行了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int N=300010;
char s[N];
LL sum[N],ans[N];
int n,m,t1[N],t2[N],c[N],sa[N],rank[N],height[N],stack[N],l[N],r[N],top,point[N],next[N],tot,en[N],b[N],fa[N],maxn[N],minn[N];
inline int in(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void add(int x,int y){
    next[++tot]=point[x];point[x]=tot;en[tot]=y;
}
inline bool cmp(int *y,int p,int q,int k){
    int o0,o1;
    o0=p+k>=n?-1:y[p+k];
    o1=q+k>=n?-1:y[q+k];
    return o0==o1&&y[p]==y[q];
}
inline void build_sa(){
    int i,k,p,*x=t1,*y=t2;
    for(m=26,i=0;i<m;++i) c[i]=0;
    for(i=0;i<n;++i) ++c[x[i]=s[i]-'a'];
    for(i=1;i<m;++i) c[i]+=c[i-1];
    for(i=n-1;~i;--i) sa[--c[x[i]]]=i;
    for(k=1;k<=n;k<<=1){
        for(p=0,i=n-k;i<n;++i) y[p++]=i;
        for(i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;
        for(i=0;i<m;++i) c[i]=0;
        for(i=0;i<n;++i) ++c[x[y[i]]];
        for(i=1;i<m;++i) c[i]+=c[i-1];
        for(i=n-1;~i;--i) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        x[sa[0]]=0;m=1;
        for(i=1;i<n;++i) x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?m-1:m++;
        if(m>=n) break;
    }
}
inline void build_height(){
    int i,j,k=0;
    for(i=0;i<n;++i) rank[sa[i]]=i;
    for(i=0;i<n;++i){
        if(!rank[i]) continue;
        k=k?k-1:k;
        j=sa[rank[i]-1];
        while(s[i+k]==s[j+k]) ++k;
        height[rank[i]]=k;
    }
}
inline int find(int x){
    if(x!=fa[x]) fa[x]=find(fa[x]);
    return fa[x];
}
int main(){
    int i,j;
    n=in();
    for(i=0;i<n;++i){
        char ch=getchar();
        while(ch<'a'||ch>'z') ch=getchar();
        s[i]=ch;
    }
    build_sa();
    build_height();
    for(i=0;i<n;++i){
        fa[i]=i;
        b[i]=minn[rank[i]]=maxn[rank[i]]=in();
    }
    stack[top=1]=0;
    for(l[0]=0,i=1;i<n;++i){
        while(top&&height[stack[top]]>height[i]) --top;
        l[i]=top?stack[top]+1:0;
        stack[++top]=i;
    }
    stack[top=1]=n-1;
    for(r[n-1]=n-1,i=n-2;i>=0;--i){
        while(top&&height[stack[top]]>=height[i]) --top;
        r[i]=top?stack[top]-1:n-1;
        stack[++top]=i;
    }
    for(i=0;i<n;++i) add(height[i],i);
    memset(ans,128,sizeof(ans));
    for(i=n-1;i;--i){
        sum[i]=sum[i+1];
        ans[i]=ans[i+1];
        for(j=point[i];j;j=next[j]){
            int o=en[j],L=l[o],R=r[o];
            sum[i]+=(LL)(o-L+1)*(LL)(R-o+1);
            int r1=find(L-1),r2=find(R);
            ans[i]=max(ans[i],max((LL)maxn[r1]*(LL)maxn[r2],(LL)minn[r1]*(LL)minn[r2]));
            fa[r2]=r1;
            maxn[r1]=max(maxn[r1],maxn[r2]);
            minn[r1]=min(minn[r1],minn[r2]);
        }
    }
    sort(b,b+n);
    ans[i]=max((LL)b[0]*(LL)b[1],(LL)b[n-1]*(LL)b[n-2]);
    sum[0]=(LL)n*(LL)(n-1)/2;
    for(i=0;i<n;++i) ans[i]=sum[i]?ans[i]:0;
    for(i=0;i<n;++i) printf("%lld %lld\n",sum[i],ans[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章