bzoj4199 [Noi2015]品酒大会(后缀数组+ 并查集 || st表 )

83 篇文章 0 订阅

bzoj4199 [Noi2015]品酒大会

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=4199

题意:
(Rinbow和Freda日常虐狗 。)
在大会的晚餐上,调酒师 Rainbow 调制了 n 杯鸡尾酒。这 n 杯鸡尾酒排成一行,其中第 i 杯酒 (1≤i≤n1≤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>1r>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相似”的酒调兑可以得到的美味度的最大值。

数据范围
n<=300000,∣ai∣≤1000000000

题解:
水题写一天系列。

题意即给一个字符串S,令Si表示字符串S从i位置起始的的后缀。定义i和j是r相似的,仅当 Si, Sj存在长度为r的公共前缀。(r相似的必然也是 r-1,r-2…0相似的)每个后缀Si有一个价值ai,定义一对后缀价值两个后缀的价值之积。求对于每个r(0~n-1),有多少对r相似的后缀,和任取一对r相似的后缀的最大价值。

SAM:
就是把原串倒过来,那么两个后缀的公共前缀变成了两个前缀的公共后缀,就是parent树的lca,
建出parent树,那么一个点作为lca的贡献就是不同子树中可作为前缀的节点的个数(就是不是nB新建的节点)乘一乘,
求最大值就同时维护个最大值&最小值,乘一乘即可。

SA:
1、ST表
我的想法简单粗暴,
既然两个后缀的lcp是这个区间内height值的最小值,那么就直接看每个后缀的height能贡献到多大的区间。
用单调栈左边找第一个<=它的+1,右边找第一个<它的-1,就找到了这个区间。

(细节挺多。这里自己理了很久。
这里之所以要这样做是因为height有相同的,
同一对点被两个位置枚举到当且仅当有height相等的挨着,为此才设计成一边<=,一边<。
同时因为两个后缀i,j的lcp其实是 [ height[sa[i]+1]height[sa[j]] ] 这个区间的最小值,
因此对于 sa[x]=i 找到的区间 l[i] r[i] ,其贡献 (il[i]+1)(r[i]i+1) 也是计算了 l[i]1 这个后缀。
也因此,如果 l[i]==1 ,没有 l[i]1 这个后缀 就不是上述贡献了。
为了少管一点细节,利用了 hetght[1]=0 ,并且把<=设计在左边,就不会有 l[i]==1 了。)

然后对于第二问,st表查一查 [ l[i]1,i1] [ i,r[i] ] 的最小最大值大大小小乘一乘即可。

2、并查集
正解代码短常数小跑得飞快真是妙妙。
正解是这样想的,单独求一个r,可以按height分组,这一组中可以任选两个,
而要求所有的r,考虑r从大到小,那么分组回越来越少,不同的组回合成一个组。

就仿佛是最初互不联通的一些点,在sa[i]和sa[i-1]间有权值为height[i]的边,
随着r从大到小,把满足要求的边加进去,当时联通的点中任选两个构成当前r的答案。

这就是一个并查集合并的过程。按height从大到小排序,依次合并,并查集维护下size最大最小值即可。

注意:
1.a[i]有正有负,所以最大最小值都要维护。
2.小心有些0-1=-1数组越界的情况。

两个代码都在下面了。

单调栈+st表 代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=300005;
const int inf=0x3f3f3f3f;
const int P=18;
int sa[N],rk[N],a[N],b[N],w[N],ht[N],mn[N][P+1],mx[N][P+1],val[N],l[N],r[N],stack[N],top=0,lg[N];
LL ans1[N],ans2[N];
char s[N];
inline int read()
{
    int ret=0,w=1; char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-'){w=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') {ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar();}
    return ret*w;
}
struct node
{
    int h,x;
    node(){}
    node(int h,int x):h(h),x(x){}
}Q[N];
bool cmp(const node &A,const node &B){return A.h!=B.h?A.h>B.h:A.x>B.x;}
void SA(int n,int m)
{
    int *x=a; int *y=b;
    for(int i=0;i<=n;i++) w[x[i]=s[i]]++;
    for(int i=1;i<=m;i++) w[i]+=w[i-1];
    for(int i=n;i>=0;i--) sa[--w[x[i]]]=i;
    for(int i=0;i<=m;i++) w[i]=0;
    for(int l=1,p=0;l<=n;l<<=1)
    {
        for(int i=n-l+1;i<=n;i++) y[p++]=i;
        for(int i=0;i<=n;i++) if(sa[i]>=l) y[p++]=sa[i]-l;
        for(int i=0;i<=n;i++) w[x[i]]++;
        for(int i=1;i<=m;i++) w[i]+=w[i-1];
        for(int i=n;i>=0;i--) sa[--w[x[y[i]]]]=y[i];
        for(int i=0;i<=m;i++) w[i]=0;
        swap(x,y); p=1; x[sa[0]]=0;
        for(int i=1;i<=n;i++) x[sa[i]]= (y[sa[i]]==y[sa[i-1]]&&y[sa[i]+l]==y[sa[i-1]+l])?p-1:p++;
        m=p; p=0; if(p>n) break;
    }
    for(int i=0;i<=n;i++) rk[sa[i]]=i; int k=0;
    for(int i=0;i<n;i++)
    {
        if(k) k--; int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k]) k++;
        ht[rk[i]]=k;
    }
}
inline int qmax(int L,int R)
{
    return max(mx[L][lg[R-L+1]],mx[R-(1<<lg[R-L+1])+1][lg[R-L+1]]);
}
inline int qmin(int L,int R)
{
    return min(mn[L][lg[R-L+1]],mn[R-(1<<lg[R-L+1])+1][lg[R-L+1]]);
}
void build(int n)
{
    lg[1]=0; for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
    for(int i=1;i<=n;i++) mn[i][0]=mx[i][0]=val[i];
    for(int p=1;p<=P;p++)
    {
        for(int i=1;i<=n;i++)
        {
            mx[i][p]=mx[i][p-1]; mn[i][p]=mn[i][p-1];
            if(i+(1<<(p-1))<=n) mx[i][p]=max(mx[i][p],mx[i+(1<<(p-1))][p-1]);
            if(i+(1<<(p-1))<=n) mn[i][p]=min(mn[i][p],mn[i+(1<<(p-1))][p-1]);
        }
    } 
    top=0;
    for(int i=1;i<=n;i++)
    {
        while(top&&ht[stack[top]]>ht[i]) top--;
        l[i]=top?stack[top]+1:1;
        stack[++top]=i;
    }top=0;
    for(int i=n;i>=1;i--)
    {
        while(top&&ht[stack[top]]>=ht[i]) top--;
        r[i]=top?stack[top]-1:n;
        stack[++top]=i;
    }
    for(int i=2;i<=n;i++)
    {
        ans1[ht[i]]+=1LL*(i-l[i]+1)*(r[i]-i+1);
        int mxl=qmax(l[i]-1,i-1); int mxr=qmax(i,r[i]);
        int mnl=qmin(l[i]-1,i-1); int mnr=qmin(i,r[i]);
        ans2[ht[i]]=max(ans2[ht[i]],max(1LL*mxl*mxr,1LL*mnl*mnr));
    }
}
int main()
{
    int n=read(); scanf("%s",s); s[n]=0;
    SA(n,128);
    for(int i=0;i<n;i++) {val[rk[i]]=read(); ans1[i]=0; ans2[i]=-1LL*inf*inf;}
    build(n); 
    for(int i=n-2;i>=0;i--) {ans1[i]+=ans1[i+1]; ans2[i]=max(ans2[i+1],ans2[i]);}
    for(int i=0;i<n;i++)
    {
        printf("%lld ",ans1[i]);
        if(ans1[i]) printf("%lld\n",ans2[i]); else  printf("0\n");
    }
    return 0;
}

并查集 代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=300005;
const int inf=0x3f3f3f3f;
int sa[N],rk[N],a[N],b[N],w[N],ht[N],fa[N],mn[N],mx[N],val[N],sz[N];
LL ans1[N],ans2[N];
char s[N];
inline int read()
{
    int ret=0,w=1; char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-'){w=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') {ret=(ret<<1)+(ret<<3)+ch-'0'; ch=getchar();}
    return ret*w;
}
struct node
{
    int h,x;
    node(){}
    node(int h,int x):h(h),x(x){}
}Q[N];
bool cmp(const node &A,const node &B){return A.h!=B.h?A.h>B.h:A.x>B.x;}
void SA(int n,int m)
{
    int *x=a; int *y=b;
    for(int i=0;i<=n;i++) w[x[i]=s[i]]++;
    for(int i=1;i<=m;i++) w[i]+=w[i-1];
    for(int i=n;i>=0;i--) sa[--w[x[i]]]=i;
    for(int i=0;i<=m;i++) w[i]=0;
    for(int l=1,p=0;l<=n;l<<=1)
    {
        for(int i=n-l+1;i<=n;i++) y[p++]=i;
        for(int i=0;i<=n;i++) if(sa[i]>=l) y[p++]=sa[i]-l;
        for(int i=0;i<=n;i++) w[x[i]]++;
        for(int i=1;i<=m;i++) w[i]+=w[i-1];
        for(int i=n;i>=0;i--) sa[--w[x[y[i]]]]=y[i];
        for(int i=0;i<=m;i++) w[i]=0;
        swap(x,y); p=1; x[sa[0]]=0;
        for(int i=1;i<=n;i++) x[sa[i]]= (y[sa[i]]==y[sa[i-1]]&&y[sa[i]+l]==y[sa[i-1]+l])?p-1:p++;
        m=p; p=0; if(p>n) break;
    }
    for(int i=0;i<=n;i++) rk[sa[i]]=i; int k=0;
    for(int i=0;i<n;i++)
    {
        if(k) k--; int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k]) k++;
        ht[rk[i]]=k;
    }
}
int getfa(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=getfa(fa[x]);
}
int main()
{
    int n=read(); scanf("%s",s); s[n]=0;
    SA(n,128);
    for(int i=0;i<n;i++) {val[rk[i]]=read(); ans1[i]=0; ans2[i]=-1LL*inf*inf;}
    for(int i=1;i<=n;i++) {mx[i]=mn[i]=val[i]; fa[i]=i; sz[i]=1; Q[i]=node(ht[i],i);}
    sort(Q+1,Q+n+1,cmp);
    for(int i=1;i<n;i++) //第n个是sa[1] y=0
    {
        int x=getfa(Q[i].x); int y=getfa(Q[i].x-1); 
        ans1[Q[i].h]+=1LL*sz[x]*sz[y];  
        ans2[Q[i].h]=max(ans2[Q[i].h],max(1LL*mx[x]*mx[y],1LL*mn[x]*mn[y]));
        fa[y]=x; mn[x]=min(mn[x],mn[y]); mx[x]=max(mx[x],mx[y]); sz[x]+=sz[y];
    }
    for(int i=n-2;i>=0;i--) {ans1[i]+=ans1[i+1]; ans2[i]=max(ans2[i+1],ans2[i]);}
    for(int i=0;i<n;i++)
    {
        printf("%lld ",ans1[i]);
        if(ans1[i]) printf("%lld\n",ans2[i]); else  printf("0\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值