[bzoj4199][Noi2015]品酒大会——后缀数组

题目大意:

给定一个序列,定义两个后缀是k相似的当且仅当这两个后缀有长度为k的公共前缀。
求对任意\(r\in [0,n-1]\),\(r\)相似的后缀的对数和两个后缀乘积的最大值。

思路:

先考虑后缀数组是如何计算两个后缀的lcp,发现是对于一段连续的height取min。
于是对于制定的相似度r,height < r的位置必定是两个后缀不能越过的,于是不难发现将有height \(\ge\)r的位置给取出来,然后整个序列分成了若干个连通块,一对具有r相似的后缀必定同时在一个联通快里面。

对于一个连通块,我们要得到答案所需要的信息即为最大值,次大值,最小值,次小值和长度。

于是直接从大到小计算答案,每次将若干个块连接,中间的过程用并查集维护。

/*=======================================
 * Author : ylsoi
 * Time : 2019.2.6
 * Problem : luogu2178
 * E-mail : [email protected]
 * ====================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<" "
#define fi first
#define se second
#define mk make_pair
#define pb push_back
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu2178.in","r",stdin);
    freopen("luogu2178.out","w",stdout);
}

template<typename T>void read(T &_){
    _=0; T fl=1; char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
    for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0');
    _*=fl;
}

const int maxn=3e5+10;
const int inf=0x3f3f3f3f;
const ll INF=2e18;
int n;
char s[maxn];
ll a[maxn];
int sz,sa[maxn],rk[maxn],tp[maxn],tax[maxn],height[maxn];

void radix_sort(){
    REP(i,1,sz)tax[i]=0;
    REP(i,1,n)++tax[rk[i]];
    REP(i,1,sz)tax[i]+=tax[i-1];
    DREP(i,n,1)sa[tax[rk[tp[i]]]--]=tp[i];
}

void suffix_sort(){
    sz=26;
    REP(i,1,n)rk[i]=s[i]-'a'+1,tp[i]=i;
    radix_sort();
    for(int w=1,p=0;w<n;w<<=1){
        p=0;
        REP(i,1,w)tp[++p]=n-w+i;
        REP(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w;
        radix_sort();
        swap(rk,tp);
        rk[sa[1]]=p=1;
        REP(i,2,n)
            if(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+w]==tp[sa[i]+w])rk[sa[i]]=p;
            else rk[sa[i]]=++p;
        sz=p;
        if(sz==n)break;
    }
    //REP(i,1,n)cout<<s+sa[i]<<endl;
    int p=0;
    REP(i,1,n){
        if(p)--p;
        int j=sa[rk[i]-1];
        while(s[i+p]==s[j+p])++p;
        height[rk[i]]=p;
    }
}

struct node{
    ll l,r,mx[2],mn[2];
}c[maxn];

node operator + (node x,node y){
    node ret;
    ret.l=min(x.l,y.l);
    ret.r=max(x.r,y.r);
    if(x.mx[0]<y.mx[0])swap(x,y);
    ret.mx[0]=x.mx[0];
    ret.mx[1]=max(x.mx[1],y.mx[0]);
    if(x.mn[0]>y.mn[0])swap(x,y);
    ret.mn[0]=x.mn[0];
    ret.mn[1]=min(x.mn[1],y.mn[0]);
    return ret;
}

int fa[maxn];

int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}

ll sum,mx=-INF;
node t;

void merge(int x,int y){
    x=find(x),y=find(y);
    //debug(x),debug(y)<<endl;
    if(x==y)return;
    sum-=(c[x].r-c[x].l+1)*(c[x].r-c[x].l+2)/2;
    sum-=(c[y].r-c[y].l+1)*(c[y].r-c[y].l+2)/2;
    c[x]=c[x]+c[y];
    sum+=(c[x].r-c[x].l+1)*(c[x].r-c[x].l+2)/2;
    t=c[c[x].l-1]+c[x];
    mx=max(mx,t.mx[0]*t.mx[1]);
    mx=max(mx,t.mn[0]*t.mn[1]);
    fa[y]=x;
}

vector<int>lis[maxn];
pair<ll,ll>ans[maxn];

void work(){
    REP(i,1,n){
        fa[i]=i;
        c[i]=(node){i,i,{a[sa[i]],-inf},{a[sa[i]],inf}};
        if(i>1)lis[height[i]].pb(i);
    }
    DREP(i,n-1,0){
        REP(j,0,lis[i].size()-1){
            ++sum;
            mx=max(mx,c[lis[i][j]].mx[0]*c[lis[i][j]-1].mx[0]);
        }
        //debug(i)<<endl;
        REP(j,0,lis[i].size()-1){
            //debug(lis[i][j]);
            if(lis[i][j]>2 && height[lis[i][j]-1]>=i)merge(lis[i][j],lis[i][j]-1);
            if(lis[i][j]<n && height[lis[i][j]+1]>=i)merge(lis[i][j],lis[i][j]+1);
        }
        //cout<<endl;
        ans[i]=mk(sum,mx);
    }
    REP(i,0,n-1)printf("%lld %lld\n",ans[i].fi,ans[i].se==-INF ? 0 : ans[i].se);
}

int main(){
    File();
    read(n);
    scanf("%s",s+1);
    REP(i,1,n)read(a[i]);
    suffix_sort();
    work();
    return 0;
}

原文地址:https://www.cnblogs.com/ylsoi/p/10353980.html

时间: 2024-10-06 14:12:48

[bzoj4199][Noi2015]品酒大会——后缀数组的相关文章

[UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集

[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 n杯鸡尾酒.这 n杯鸡尾酒排成一行,其中第 i杯酒 (1≤i≤n ) 被贴上了一个标签 si ,每个标签都是 26 个小写英文字母之一.设 Str(l,r)表示第 l杯酒到第 r 杯酒的 r−l+1 个标签顺次连接构成的字

【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集

[BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默水过~ 本题的实现还是非常简单的,先求出height数组,然后两杯酒'r'相似就等价于二者中间的height都>=r,于是我们将height排序,从大到小扔进去,那么所有连续的区间中的点对就都是相似的了.维护连续区间可以用并查集.统计点对个数需要维护size,统计最大乘积需要维护最(次)大(小)值,

【BZOJ-4199】品酒大会 后缀数组 + 并查集合并集合

4199: [Noi2015]品酒大会 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 436  Solved: 243[Submit][Status][Discuss] Description 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 i

BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )

求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ----------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ty

Uoj #131. 【NOI2015】品酒大会 后缀数组,并查集

#131. [NOI2015]品酒大会 统计 描述 提交 自定义测试 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainbow 调制了 nn 杯鸡尾酒.这 nn 杯鸡尾酒排成一行,其中第 ii 杯酒 (1≤i≤n1in) 被贴上了一个标签 sisi,每个标签都是 2626 个小写英文字母之一.设 Str(l,r)Strlr 表示第 ll 杯酒到第 rr 杯酒的 

BZOJ4199: [Noi2015]品酒大会

后缀数组height排序后并查集合并 也就是height较大的合并不影响较小的 num[i]=num[i+1]  ans[i]=ans[i+1] 合并时,num+=sz[x]*sz[y],ans=max(mn[x]*mn[y],mx[x]*mx[y],ans) 这种思路适应于求点对,还可以考虑启发式合并 后缀数组还有的常见思路就是二分,height分组 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 300005 4 #

bzoj4199: [Noi2015]品酒大会 (并查集 &amp;&amp; 后缀数组)

据说用后缀自动机 + dp也能做 然而并不会 后缀数组的做法呢 就是先建个后缀数组,求出height值,此时如果直接找,复杂度是n ^ 2的,肯定会超时. 但是height大的值是不会对小的产生影响的,所以可以按height大小,从大到小合并两个区间,用并查集维护就可以了 代码如下 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 using namespace std; 5 const

字符串(后缀数组||SAM):NOI2015 品酒大会

这道题可以转化为计数类问题. 若使用后缀数组,那么答案就是所有位置二元组(i,j)的lcp对0~lcp答案段的贡献.然后发现若一个二元组有x的贡献,那么对x-1有同样的贡献,考虑先求出lcp(max)的答案,再传给lcp(max-1)等等,复杂度是O(N)的. 若用SAM,那么需要求的答案在x与fa[x]的转移之间,因为后缀自动机中每个点所代表的出现位置和出现次数是相等的,可以合并,事实上还是用到了后缀数组的叠加的原理,从叶子节点一路传上去…………………………………………… 还有要注意INF要足

Bzoj4199:[NOI2015]品酒大会

题面 Bzoj4199 Sol 后缀数组 显然的暴力就是求\(LCP\)+差分 \(40\)分 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long ll; const int _(3e5 + 5); IL int Input(){ RG int x =