BZOJ4199:[NOI2015]品酒大会——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4199

https://www.luogu.org/problemnew/show/P2178#sub

http://uoj.ac/problem/131

一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加。

在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 n 杯酒 (1 ≤ i ≤ n) 被贴上了一个标签si,每个标签都是 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) 相似”的。特别地,对于任意的 1 ≤ p , q ≤ n , p ≠ q ,第 p 杯酒和第 q 杯酒都 是“ 0 相似”的。

在品尝环节上,品酒师 Freda 轻松地评定了每一杯酒的美味度,凭借其专业的水准和经验成功夺取了“首席品酒家”的称号,其中第 i 杯酒 (1 ≤ i ≤ n) 的 美味度为 ai 。现在 Rainbow 公布了挑战环节的问题:本次大会调制的鸡尾酒有一个特点,如果把第 p 杯酒与第 q 杯酒调兑在一起,将得到一杯美味度为 ap*aq 的 酒。现在请各位品酒师分别对于 r = 0,1,2, ⋯ , n − 1 ,统计出有多少种方法可以 选出 2 杯“ r 相似”的酒,并回答选择 2 杯“ r 相似”的酒调兑可以得到的美味度的最大值。

参考:https://blog.csdn.net/alxpcun/article/details/50482493

题意很绕,看了半个小时没看懂求助题解得到了一个言简意赅的题意:

后缀都有价值,找到两个后缀,他们的公共前缀长为r则称他们"r"相似,求对于所有r取值有多少个"r"相似以及两个后缀价值积最大。

暴力:枚举r,用height数组分块,对于大小为n的块显然是有n*(n-1)/2对合法解,同时维护块的最大次大值和最小次小值来更新答案即可。

(因为价值有负数,所以不仅要考虑最大值的乘积,还要考虑最小值的乘积。)

正解:我们知道height小值会影响height大值的影响,换言之大值不会影响小值。

所以我们对height数组排序,从后往前枚举r,每次继承前一个r的所有答案开始更新。

用并查集维护一下当前height的后缀之间的关系,这样在整个并查集里面都是两两为"r"相似的对。

#include<cstdio>
#include<cmath>
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int N=3e5+10;
const ll INF=1e18;
char s[N];
int n,m,rk[N],sa[N],w[N],fa[N],height[N];
ll a[N],ans[N][2],size[N],maxn[N],minn[N];
struct node{
    int h,x,y;
}g[N];
inline bool cmp(node wa,node wb){
    return wa.h>wb.h;
}
inline bool pan(int *x,int i,int j,int k){
    int ti=i+k<n?x[i+k]:-1;
    int tj=j+k<n?x[j+k]:-1;
    return ti==tj&&x[i]==x[j];
}
void SA_init(){
    int *x=rk,*y=height,r=256;
    for(int i=0;i<r;i++)w[i]=0;
    for(int i=0;i<n;i++)w[s[i]]++;
    for(int i=1;i<r;i++)w[i]+=w[i-1];
    for(int i=n-1;i>=0;i--)sa[--w[s[i]]]=i;
    r=1;x[sa[0]]=0;
    for(int i=1;i<n;i++)
    x[sa[i]]=s[sa[i]]==s[sa[i-1]]?r-1:r++;
    for(int k=1;r<n;k<<=1){
    int yn=0;
    for(int i=n-k;i<n;i++)y[yn++]=i;
    for(int i=0;i<n;i++)
        if(sa[i]>=k)y[yn++]=sa[i]-k;
    for(int i=0;i<r;i++)w[i]=0;
    for(int i=0;i<n;i++)w[x[y[i]]]++;
    for(int i=1;i<r;i++)w[i]+=w[i-1];
    for(int i=n-1;i>=0;i--)sa[--w[x[y[i]]]]=y[i];
    swap(x,y);r=1;x[sa[0]]=0;
    for(int i=1;i<n;i++)
        x[sa[i]]=pan(y,sa[i],sa[i-1],k)?r-1:r++;
    }
}
void height_init(){
    int i,j,k=0;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++){
    if(k)k--;
    int j=sa[rk[i]-1];
    while(s[i+k]==s[j+k])k++;
    height[rk[i]]=k;
    }
}
int find(int x){
    if(x==fa[x])return x;
    return fa[x]=find(fa[x]);
}
void unionn(int x,int y){
    if(size[x]>size[y])swap(x,y);
    fa[x]=y,size[y]+=size[x];
    maxn[y]=max(maxn[x],maxn[y]);
    minn[y]=min(minn[x],minn[y]);
}
void solve(){
    for(int i=2;i<=n;i++)g[i-1]=(node){height[i],i,i-1};
    sort(g+1,g+n,cmp);
    for(int i=g[1].h,j=1;i>=0;i--){
    ans[i][0]=ans[i+1][0];ans[i][1]=ans[i+1][1];
    for(;j<n&&g[j].h==i;j++){
        int x=find(g[j].x),y=find(g[j].y);
        if(x==y)continue;
        ans[i][1]=max(ans[i][1],maxn[x]*maxn[y]);
        ans[i][1]=max(ans[i][1],minn[x]*minn[y]);
        ans[i][0]+=size[x]*size[y];
        unionn(x,y);
    }
    }
}
int main(){
    scanf("%d%s",&n,s);
    for(int i=0;i<=n;i++)ans[i][1]=-INF;
    s[n++]=0;
    SA_init();
    n--;
    height_init();
    for(int i=1;i<=n;i++){
    scanf("%lld",&a[i]);
    maxn[rk[i-1]]=a[i],minn[rk[i-1]]=a[i];
    fa[i]=i;size[i]=1;
    }
    solve();
    for(int i=0;i<n;i++)
    printf("%lld %lld\n",ans[i][0],ans[i][0]?ans[i][1]:0);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-03-28 10:56  luyouqi233  阅读(226)  评论(0编辑  收藏  举报