题意

因为一个\(k\)相似必定为\(k-1,k-2....0\)相似,对于一个\(lcp\)为\(k\)后缀对\((i,j)\),我们只用把它的贡献加在\(k\)的答案上,最后求一个后缀和和后缀max就可以得到答案。

考虑如何快速计算后缀对的贡献:

因为后缀对\((i,j),i>j\)的\(lcp\)是\(min_{k=i+1}^{j}height_k\),因此考虑将\(height\)从大到小排序。

对于当前的\(height_i\),我们找到\(sa_{i-1}\)和\(sa_i\)所在后缀集合(一开始每个后缀是单独一个集合)。这时两个集合分别选两个后缀配对,\(lcp\)必定为\(height_i\),于是\(ans[height_i]\)就加上两集合大小的乘积,之后合并两个集合。第一问就做完了。

第二问只需要对每个集合维护最大值和最小值(负数乘负数会变成正数),合并时取个max即可。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3*1e5+10;
int n,num;
int fa[maxn],size[maxn];
ll a[maxn],maxx[maxn],minn[maxn],ans1[maxn],ans2[maxn];
char s[maxn];
struct node{int height,id;}h[maxn];
struct SA
{
int num;
int sa[maxn],rk[maxn],oldrk[maxn],id[maxn],tmpid[maxn],cnt[maxn],height[maxn];
inline bool check(int x,int y,int k){return oldrk[x]==oldrk[y]&&oldrk[x+k]==oldrk[y+k];}
inline void build(char* s,int len)
{
int num=300;
for(int i=1;i<=len;i++)cnt[rk[i]=s[i]]++;
for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
for(int i=len;i;i--)sa[cnt[rk[i]]--]=i;
for(int t=1;t<=len;t<<=1)
{
int tot=0;
for(int i=len-t+1;i<=len;i++)id[++tot]=i;
for(int i=1;i<=len;i++)if(sa[i]>t)id[++tot]=sa[i]-t;
tot=0;
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=len;i++)cnt[tmpid[i]=rk[id[i]]]++;
for(int i=1;i<=num;i++)cnt[i]+=cnt[i-1];
for(int i=len;i;i--)sa[cnt[tmpid[i]]--]=id[i];
memcpy(oldrk,rk,sizeof(rk));
for(int i=1;i<=len;i++)rk[sa[i]]=check(sa[i-1],sa[i],t)?tot:++tot;
num=tot;
if(num==len)break;
}
for(int i=1,j=0;i<=len;i++)
{
if(j)j--;
while(s[i+j]==s[sa[rk[i]-1]+j])j++;
height[rk[i]]=j;
}
}
}Sa;
inline ll read()
{
char c=getchar();ll res=0,f=1;
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
inline bool cmp(node x,node y){return x.height>y.height;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y,int k)
{
int p=find(x),q=find(y);
if(size[p]>size[q])swap(p,q);
ans1[k]+=1ll*size[p]*size[q];
ans2[k]=max(ans2[k],max(maxx[p]*maxx[q],minn[p]*minn[q]));
minn[q]=min(minn[q],minn[p]);
maxx[q]=max(maxx[q],maxx[p]);
fa[p]=q;size[q]+=size[p];
}
int main()
{
n=read();
scanf("%s",s+1);
Sa.build(s,n);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)fa[i]=i,size[i]=1,maxx[i]=minn[i]=a[i];
for(int i=2;i<=n;i++)h[i]=(node){Sa.height[i],i};
sort(h+2,h+n+1,cmp);
memset(ans2,-0x3f,sizeof(ans2));
for(int i=2;i<=n;i++)merge(Sa.sa[h[i].id-1],Sa.sa[h[i].id],h[i].height);
for(int i=n-1;~i;i--)ans1[i]+=ans1[i+1],ans2[i]=max(ans2[i],ans2[i+1]);
for(int i=0;i<n;i++)
if(ans1[i])printf("%lld %lld\n",ans1[i],ans2[i]);
else puts("0 0");
return 0;
}

luoguP2178 [NOI2015]品酒大会(后缀数组做法)的更多相关文章

  1. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

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

    [UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...

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

    [BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默 ...

  4. [NOI2015] 品酒大会 - 后缀数组,并查集,STL,启发式合并

    [NOI2015] 品酒大会 Description 对于每一个 \(i \in [0,n)\) 求有多少对后缀满足 LCP 长度 \(\le i\) ,并求满足条件的两个后缀权值乘积的最大值. So ...

  5. BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)

    BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...

  6. NOI2015品酒大会 后缀数组

    自己尝试敲后缀数组,发现难看(tiao)的不行,于是抄了板子 考虑建出hei以后转化出的问题: 对于一个数组中权值大于等于k的连续部分,求取两个数的方案数和两数积的最大值 (好气啊,可以有负数) 把询 ...

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

    求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ------------------------- ...

  8. luoguP2178 [NOI2015]品酒大会(后缀自动机)

    题意 承接上篇题解 考虑两个后缀的\(lcp\)是什么,是将串反着插入后缀自动机后两个前缀(终止节点)的\(lca\)!!!于是可以在parent tree上DP了. 比后缀数组又简单又好写跑的还快. ...

  9. 【学术篇】NOI2015 品酒大会 后缀数组+并查集

    省选前大致是刷不了几道题了... 所以就找一些裸一点的题目练练板子算了= = 然而这题一点都不裸, 也并不怎么好写... 于是就浪费了将近一下午的时间... 然而还不是因为后缀数组板子不熟= = 首先 ...

随机推荐

  1. acwing 116. 飞行员兄弟

    地址  https://www.acwing.com/problem/content/118/ “飞行员兄弟”这个游戏,需要玩家顺利的打开一个拥有16个把手的冰箱. 已知每个把手可以处于以下两种状态之 ...

  2. 【oracle】表和索引建立在不用表空间原因

    磁盘I/O竞争,要放在[真]的不同的磁盘上. Oracle强烈建议,任何一个应用程序的库表至少需要创建两个表空间,其中之一用于存储表数据,而另一个用于存储表索引数据.因为如果将表数据和索引数据放在一起 ...

  3. [学习笔记] Manacher与PAM

    \(1\) Manacher 挺短,背是挺好背的 Manacher用于求回文串长度.思想大概就是: 1.加入字符集之外的识别字符(比如#)分隔开原来相邻的字母,这样所有的回文串都变成了以某个字符为中心 ...

  4. jsp模板

    <%String path = request.getContextPath();String basePath = request.getScheme()+"://"+re ...

  5. C#上手练习7(方法语句2)

    上一篇方法调用赋值封装,这里使用封装后调用,尽量满足开闭原则. 以及静态类的使用. using System; namespace KingTest03 { class Program { int a ...

  6. 【踩坑系列】VS2019提示 ' the package could not be found in c\users\username\nuget\packages\. '

    解决步骤 1.删除对应项目下的 obj 文件夹 2.重新生成该项目

  7. Web前端基础(1):HTML(一)

    1. HTML概述 1.1 什么是HTML HTML称为超文本标记语言,是一种标识性的语言.它包括一系列标签.通过这些标签可以将网络上的文档格式统一,使分散的Internet资源连接为一个逻辑整体.H ...

  8. vscode 设置代码格式化缩进为2个空格

    打开文件——>首选——>设置 输入搜索 tabsize 按照下图设置即可,然后打开 注意:如果不将Detect Indentation 勾选取消 以前用tab创建的忘记依然为4个空格

  9. Node.js提供了哪些内容(API)

    Nodejs运行时,JavaScript代码运行时的环境. 提供了一些核心模块,应用程序编程接口(Application Program Interface,API)  API 的意思就是.一个已经解 ...

  10. elasticsearch ik同义词

    由于elasticsearch 更新实在太快,配置同义词的资料层次不齐,费尽千辛万苦终于找到了.本文通过一个同义词搜索的简单实例来说明ik同义词的配置. 环境介绍 这点很重要,本文是基于elastic ...