[bzoj4199][Noi2015]品酒大会——后缀数组
Posted ylsoi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj4199][Noi2015]品酒大会——后缀数组相关的知识,希望对你有一定的参考价值。
题目大意:
给定一个序列,定义两个后缀是k相似的当且仅当这两个后缀有长度为k的公共前缀。
求对任意(rin [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
",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;
}
以上是关于[bzoj4199][Noi2015]品酒大会——后缀数组的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ4199[Noi2015]品酒大会 后缀数组+并查集