[bzoj4199][后缀数组][并查集]品酒大会

18 篇文章 0 订阅
5 篇文章 0 订阅

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 相似”的酒调兑可以 得到的美味度的最大值。

Input

输入文件的第1行包含1个正整数 n ,表示鸡尾酒的杯数。 第 2 行包含一个长度为 n 的字符串 S ,其中第 i 个字符表示第 i
杯酒的标签。 第 3 行包含 n 个整数,相邻整数之间用单个空格隔开,其中第 i 个整数表示第 i 杯酒的美味度 a_i 。
n=300,000 |a_i |≤1,000,000,000

Output

输出文件包括 n 行。第 i 行输出 2 个整数,中间用单个空格隔开。

第 1 个整数表示选出两杯“ (i-1)” ” 相似”的酒的方案数, 第 2 个整数表示选出两杯“ (i-1)
相似”的酒调兑可以得到的最大美味度。 若不存在两杯“ (i-1) 相似”的酒,这两个数均为 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

【样例说明1】

用二元组 (p,q) 表示第 p 杯酒与第 q 杯酒。

0 相似:所有 45 对二元组都是 0 相似的,美味度最大的是 8×7=56 。

1 相似: (1,8) (2,4) (2,9) (4,9) (5,6) (5,7) (5,10) (6,7) (6,10) (7,10)
,最大的 8×7=56 。

2 相似: (1,8) (4,9) (5,6) ,最大的 4×8=32 。

没有 3,4,5,?,9 相似的两杯酒,故均输出 0 。

题解

不让 log2 l o g 2 的SA过的题都不是好题哼
很显然是求任意两个后缀的最长公共前缀
先把SA和height跑出来
倒着离线
任意两个后缀的最长公共前缀就是他们连续一段的height的最小值
枚举答案,每次把height>=枚举的数的位置标为true,如果连成一段了就可以更新答案呀。
维护一个最大次大和最小次小。因为数可能是负数
并查集优化就过了…
哼不让 log2 l o g 2 过的人就是坏淫

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define inf (1<<30)
#define LL long long
#define ULL unsigned long long
#define HA 569
using namespace std;
inline int read()
{
    int f=1,x=0;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 write(LL x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
inline void print(LL x){write(x);putchar(' ');}
struct node
{
    LL g1,g2;
    node(){}
    node(LL _g1,LL _g2){g1=_g1;g2=_g2;}
    friend bool operator <(node n1,node n2){return n1.g1*n1.g2<n2.g1*n2.g2;}
};
struct gggg
{
    LL g1,g2;
    gggg(){}
    gggg(LL _g1,LL _g2){g1=_g1;g2=_g2;}
    friend bool operator <(gggg n1,gggg n2){return n1.g1*n1.g2<n2.g1*n2.g2;}
};
priority_queue<node> heap;
priority_queue<gggg> pppp;
vector<int> li[300005];
LL a1[300005],a2[300005];
char ch[300005];
ULL hash[300005],pre[300005];
int p[300005],height[300005],a[300005],tmp[300005],n;
int Rsort[300005],Rank[300005],sa1[300005],sa2[300005];
int tt[300005];
void get_sa(int len,int m)
{
    memcpy(Rank,tmp,sizeof(Rank));
    memset(Rsort,0,sizeof(Rsort));
    for(int i=1;i<=len;i++)Rsort[Rank[i]]++;
    for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
    for(int i=len;i>=1;i--)sa1[Rsort[Rank[i]]--]=i;
    int ln=1,p=0;
    while(p<len)
    {
        int k=0;
        for(int i=len-ln+1;i<=len;i++)sa2[++k]=i;
        for(int i=1;i<=len;i++)if(sa1[i]-ln>0)sa2[++k]=sa1[i]-ln;

        memset(Rsort,0,sizeof(Rsort));
        for(int i=1;i<=len;i++)Rsort[Rank[i]]++;
        for(int i=1;i<=m;i++)Rsort[i]+=Rsort[i-1];
        for(int i=len;i>=1;i--)sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];

        memcpy(tt,Rank,sizeof(tt));
        Rank[sa1[1]]=1;p=1;
        for(int i=2;i<=len;i++)
        {
            if(tt[sa1[i]]!=tt[sa1[i-1]] || tt[sa1[i]+ln]!=tt[sa1[i-1]+ln])p++;
            Rank[sa1[i]]=p;
        }
        ln*=2;m=p;
    }
}
/*inline bool check(int u,int v,int ln)
{
    ULL gg=pre[ln];
    return hash[u+ln-1]-hash[u-1]*gg==hash[v+ln-1]-hash[v-1]*gg;
}
inline bool cmp(int n1,int n2)
{
    int l=1,r=n,ret=0;//二分长度 
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(mid>min(n-n1+1,n-n2+1))r=mid-1;
        if(!check(n1,n2,mid))r=mid-1;
        else ret=mid,l=mid+1;
    }
    return ch[n1+ret]-'a'<ch[n2+ret]-'a';
}*/
inline void gethe()
{
    int j,k=0;
    for(int i=1;i<=n;i++)
    {
        j=p[Rank[i]-1];
        if(k)k--;
        while(ch[i+k]==ch[j+k])k++;
        height[Rank[i]]=k;
    }
}
bool v[300005];
int fa[300005],tot[300005],mx[300005][2],mn[300005][2],gg[5];
int findfa(int x){return fa[x]==x?fa[x]:fa[x]=findfa(fa[x]);}
inline void meg(int u,int v)
{
    fa[u]=v;tot[v]+=tot[u];
    gg[1]=mx[u][0],gg[2]=mx[u][1],gg[3]=mx[v][0],gg[4]=mx[v][1];sort(gg+1,gg+1+4);
    mx[v][0]=gg[4];mx[v][1]=gg[3];
    gg[1]=mn[u][0],gg[2]=mn[u][1],gg[3]=mn[v][0],gg[4]=mn[v][1];sort(gg+1,gg+1+4);
    mn[v][0]=gg[1];mn[v][1]=gg[2];
}
LL aaa;
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    n=read();
    scanf("%s",ch+1);
/*  pre[0]=1;
    for(int i=1;i<=n;i++)
    {
        hash[i]=hash[i-1]*HA+ch[i]-'a';
        pre[i]=pre[i-1]*HA;p[i]=i;
    }
    sort(p+1,p+1+n,cmp);*/
    for(int i=1;i<=n;i++)tmp[i]=ch[i]-'a'+1;
    get_sa(n,30);
    for(int i=1;i<=n;i++)p[i]=sa1[i],Rank[p[i]]=i;

    for(int i=1;i<=n;i++)a[i]=read();
    gethe();memset(v,false,sizeof(v));
/*  for(int i=1;i<=n;i++)
    {
        for(int j=p[i];j<=n;j++)printf("%c",ch[j]);
        puts("");
        printf("      HEIGHT : %d\n",height[i]);
    }*/
    for(int i=1;i<=n;i++)li[height[i]].push_back(i),fa[i]=i,tot[i]=1,mx[i][0]=mn[i][0]=a[p[i]],mx[i][1]=-inf,mn[i][1]=inf;
    LL sum=0;aaa=-(1LL<<63-1);
    for(int i=n-1;i>=0;i--)
    {
        for(int j=0;j<li[i].size();j++)
        {
            int po=li[i][j];v[po]=true;sum++;
            if(po==1)sum--;
            if(v[po-1])
            {
                int pp=findfa(po),q=findfa(po-1);
                if(tot[q]!=po-1)sum+=((LL)tot[pp]*(tot[q]+1)-tot[pp]);
                else sum+=((LL)tot[pp]*tot[q]-tot[pp]);
                int la=tot[q];
                meg(pp,q);
            /*  heap.push(node(mx[q][0],max(mx[q][1],a[p[po-1-la]])));
                pppp.push(gggg(mn[q][0],min(mn[q][1],a[p[po-1-la]])));*/
                aaa=max(aaa,max((LL)mx[q][0]*max(mx[q][1],a[p[po-1-la]]),(LL)mn[q][0]*min(mn[q][1],a[p[po-1-la]])));
            }
            if(v[po+1])
            {
                int pp=findfa(po),q=findfa(po+1);
                if(tot[pp]!=po)sum+=((LL)(tot[pp]+1)*tot[q]-tot[q]);
                else sum+=((LL)tot[pp]*tot[q]-tot[q]);
                int la=tot[pp];
                meg(pp,q);
                /*heap.push(node(mx[q][0],max(mx[q][1],a[p[po-la]])));
                pppp.push(gggg(mn[q][0],min(mn[q][1],a[p[po-la]])));*/
                aaa=max(aaa,max((LL)mx[q][0]*max(mx[q][1],a[p[po-la]]),(LL)mn[q][0]*min(mn[q][1],a[p[po-la]])));
            }
            if(!v[po-1]&&!v[po+1])
            {
                /*heap.push(node(mx[po][0],max(a[p[po-1]],mx[po][1])));
                pppp.push(gggg(mn[po][0],min(mn[po][1],a[p[po-1]])));*/
                aaa=max(aaa,max((LL)mx[po][0]*max(mx[po][1],a[p[po-1]]),(LL)mn[po][0]*min(mn[po][1],a[p[po-1]])));
            }
        }
        a1[i]=sum;
        if(aaa!=-(1LL<<63-1))a2[i]=aaa;
    }
    for(int i=0;i<n;i++)
    {
        print(a1[i]);print(a2[i]);
        puts("");
        //printf("%lld %lld\n",a1[i],a2[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值