[bzoj 4199][NOI 2015]品酒大会

传送门

Description

一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品

酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。在大会的晚餐上,调酒师Rainbow调制了 n 杯鸡尾酒。

这 n 杯鸡尾酒排成一行,其中第 i 杯酒 \((1≤i≤n)\) 被贴上了一个标签 \(s_i\),每个标签都是 26 个小写英文字母

之一。设 Str(l,r) 表示第 l 杯酒到第 r 杯酒的 r-l+1 个标签顺次连接构成的字符串。若 \(Str(p,po)=Str(q,qo)\) ,其中 \(1≤p≤po≤n,1≤q≤qo≤n,p≠q,po-p+1=qo-q+1=r\) ,则称第 p 杯酒与第 q 杯酒是“ r 相似”的。当

然两杯“ r 相似”(r>1)的酒同时也是“ 1 相似”、“ 2 相似”、……、“ (r-1) 相似”的。在品尝环节上,

品酒师Freda轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中

第 i 杯酒 (1≤i≤n) 的美味度为 \(a_i\) 。现在Rainbow公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点

,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 \(a_p ,a_q\) 的酒。现在请各位品酒师分别对于 \(r=0,1,2,..,n-1\) ,统计出有多少种方法可以选出 2 杯“ r 相似”的酒,并回答选择 2 杯“ r 相似”的酒调兑可以

得到的美味度的最大值。

Solution

构建出\(height\)数组,然后自上而下合并即可,因为权值有可能是负数,所以对于每个集合,需要维护一下它的最大值和最小值。


Code 

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
    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<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
#define MN 300005
#define M(a) memset(a,0,sizeof a);
int n,l[MN],r[MN],siz[MN],fa[MN],id[MN];char s[MN];
ll m[MN],ans[MN];
int h[MN],p,q,sa[2][MN],rk[2][MN],num[MN];
inline void build_sa()
{
    register int i,j,k,mx;
    for(i=1;i<=n;++i) num[s[i]-'a'+1]++;
    for(i=1;i<=26;++i) num[i]+=num[i-1];
    for(i=1;i<=n;++i) sa[1][num[s[i]-'a'+1]--]=i;
    for(i=1;i<=n;++i) rk[1][sa[1][i]]=rk[1][sa[1][i-1]]+(s[sa[1][i-1]]!=s[sa[1][i]]);
    mx=rk[1][sa[1][n]];
    for(p=1,q=0,k=1;k<=n;k<<=1,p^=1,q^=1)
    {
        if(mx==n) break;
        for(i=1;i<=n;++i) num[rk[p][sa[p][i]]]=i;
        for(i=n;i;--i) if(sa[p][i]>k) sa[q][num[rk[p][sa[p][i]-k]]--]=sa[p][i]-k;
        for(i=n-k+1;i<=n;++i) sa[q][num[rk[p][i]]--]=i;
        for(i=1;i<=n;++i)
        rk[q][sa[q][i]]=rk[q][sa[q][i-1]]+(rk[p][sa[q][i]]!=rk[p][sa[q][i-1]]||rk[p][sa[q][i]+k]!=rk[p][sa[q][i-1]+k]);
        mx=rk[q][sa[q][n]];
    }
    for(i=k=1;i<=n;++i)
    {
        if(rk[p][i]==1) continue;if(k) k--;
        for(j=sa[p][rk[p][i]-1];j+k<=n&&i+k<=n&&s[i+k]==s[j+k];++k);
        h[i]=k;
    }
}
inline bool cmp(const int&o,const int&oo){return h[o]>h[oo];}
inline int getf(int x){return fa[x]==x?x:fa[x]=getf(fa[x]);}
int main()
{
    n=read();scanf("%s",s+1);register int i;
    for(i=1;i<=n;++i) l[i]=r[i]=read();
    for(i=1;i<=n;++i) id[i]=i,ans[i-1]=-1e18,siz[i]=1,fa[i]=i;
    build_sa();std::sort(id+1,id+n+1,cmp);
    for(i=1;i<=n;i++)
    {
        int u=getf(id[i]),v=getf(sa[p][rk[p][id[i]]-1]);
        m[h[id[i]]]+=1ll*siz[u]*siz[v],ans[h[id[i]]]=max(ans[h[id[i]]],max(1ll*l[u]*l[v],1ll*r[u]*r[v]));
        r[u]=max(r[u],r[v]),l[u]=min(l[u],l[v]),siz[u]+=siz[v],fa[v]=u;
    }
    for(i=n-2;i>=0;i--) m[i]+=m[i+1],ans[i]=max(ans[i],ans[i+1]);
    for(i=0;i<n;i++) printf("%lld %lld\n",m[i],m[i]?ans[i]:0);
    return 0;
}



Blog来自PaperCloud,未经允许,请勿转载,TKS!

转载于:https://www.cnblogs.com/PaperCloud/p/10281044.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值