4199. [NOI2015]品酒大会【后缀数组+并查集】
Description
一年一度的“幻影阁夏日品酒大会”隆重开幕了。大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品
Input
Output
输出文件包括 n 行。第 i 行输出 2 个整数,中间用单个空格隔开。
Sample Input
ponoiiipoi
2 1 4 7 4 8 3 6 4 7
Sample Output
10 56
3 32
0 0
0 0
0 0
0 0
0 0
0 0
0 0
【样例说明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 。
罕见的抄了一发题解……毕竟NOI原题哪有那么容易写出来的道理
并没有什么罕见的算法,不过思路还是很巧妙的
一开始我的想法是用单调栈,而且好像的确有这种算法的std,不过我乱搞了一下午样例都没过于是只好作罢
改为大众并查集做法。
首先很容易发现,对于任意一对r相似,它一定是k(0<k<r)相似的
所以求出height数组后按其中的值排序,然后从大到小做
当前需要处理的串为i和i-1,设前缀长度为k
易知若将两个并查集合并,则当前的前缀在并查集中一定是最小的,所以Ans[k][0]+=两棵树size的乘积(因为任意两两前缀都是k相似的,可以配对)
除了并查集的size,还维护一下并查集的max和min值,
则Ans[k][1]=max(Ans[k][1],Max1*Max2,Min1*Min2)
维护min值是为了防止有很小的复数这种情况(负负得正)
最后因为Ans[i]也是满足Ans[i+1]的,所以做个前缀和合并一下答案就好
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN (300000+100)
using namespace std;
int wt[MAXN],wa[MAXN],wb[MAXN];
int SA[MAXN],Rank[MAXN],Height[MAXN];
char r[MAXN];
int a[MAXN],cnt;
int ID[MAXN],Father[MAXN];
long long Size[MAXN],Max[MAXN],Min[MAXN],Ans[MAXN][],INF;
int n,m=; bool cmp(int *y,int a,int b,int k)
{
int arank1=y[a];
int brank1=y[b];
int arank2=a+k>=n?-:y[a+k];
int brank2=b+k>=n?-:y[b+k];
return arank1==brank1 && arank2==brank2;
} void Build_SA()
{
int *x=wa,*y=wb;
for (int i=;i<m;++i) wt[i]=;
for (int i=;i<n;++i) wt[x[i]=r[i]]++;
for (int i=;i<m;++i) wt[i]+=wt[i-];
for (int i=n-;i>=;--i) SA[--wt[x[i]]]=i; for (int j=;j<=n;j<<=)
{
int p=;
for (int i=n-j;i<n;++i) y[p++]=i;
for (int i=;i<n;++i) if (SA[i]>=j) y[p++]=SA[i]-j; for (int i=;i<m;++i) wt[i]=;
for (int i=;i<n;++i) wt[x[y[i]]]++;
for (int i=;i<m;++i) wt[i]+=wt[i-];
for (int i=n-;i>=;--i) SA[--wt[x[y[i]]]]=y[i]; m=;swap(x,y);
x[SA[]]=;
for (int i=;i<n;++i)
x[SA[i]]=cmp(y,SA[i],SA[i-],j)?m-:m++;
if (m>=n) break;
}
} void Build_Height()
{
for (int i=;i<n;++i) Rank[SA[i]]=i;
int k=;
Height[]=;
for (int i=;i<n;++i)
{
if (!Rank[i]) continue;
if (k) k--;
int j=SA[Rank[i]-];
while (r[i+k]==r[j+k]) k++;
Height[Rank[i]]=k;
}
} int Find (int x) {return Father[x]==x?x:Father[x]=Find(Father[x]);}
void Merge (int x,int y)
{
int f1=Find(x),f2=Find(y); int k=Height[x];
Ans[k][]+=Size[f2]*Size[f1];
Ans[k][]=max(max(Max[f2]*Max[f1],Min[f2]*Min[f1]),Ans[k][]); Min[f1]=min(Min[f1],Min[f2]);
Max[f1]=max(Max[f1],Max[f2]);
Father[f2]=f1;
Size[f1]+=Size[f2];
} void Solve()
{
for (int i=;i<n;++i)
{
Father[i]=i;
Size[i]=;
Max[i]=Min[i]=a[SA[i]];
}
for (int i=;i<=n-;++i)
if (Find(ID[i])!=Find(ID[i]-))
Merge(ID[i],ID[i]-);
} bool cmp1(int x,int y)
{
return Height[x]>Height[y];
} int main()
{
memset(&INF,0x7f,sizeof(INF));
scanf("%d",&n);
scanf("%s",r);
for (int i=;i<n;++i)
scanf("%d",&a[i]);
Build_SA();
Build_Height();
for (int i=;i<n;++i)
{
ID[i]=i;
Ans[i][]=;
Ans[i][]=-INF;
}
sort(ID,ID+n,cmp1);
Solve();
for (int i=n-;i>=;--i)
{
Ans[i][]+=Ans[i+][];
Ans[i][]=max(Ans[i][],Ans[i+][]);
}
for (int i=;i<n;++i)
printf("%lld %lld\n",Ans[i][],Ans[i][]==?:Ans[i][]);
}
4199. [NOI2015]品酒大会【后缀数组+并查集】的更多相关文章
- BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )
求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ------------------------- ...
- [UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集
[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...
- 【BZOJ4199】[Noi2015]品酒大会 后缀数组+并查集
[BZOJ4199][Noi2015]品酒大会 题面:http://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=2144 题解:听说能用SAM?SA默默 ...
- [NOI2015] 品酒大会 - 后缀数组,并查集,STL,启发式合并
[NOI2015] 品酒大会 Description 对于每一个 \(i \in [0,n)\) 求有多少对后缀满足 LCP 长度 \(\le i\) ,并求满足条件的两个后缀权值乘积的最大值. So ...
- 【学术篇】NOI2015 品酒大会 后缀数组+并查集
省选前大致是刷不了几道题了... 所以就找一些裸一点的题目练练板子算了= = 然而这题一点都不裸, 也并不怎么好写... 于是就浪费了将近一下午的时间... 然而还不是因为后缀数组板子不熟= = 首先 ...
- BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]
4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...
- NOI 2015 品酒大会 (后缀数组+并查集)
题目大意:略 40分暴力还是很好写的,差分再跑个后缀和 和 后缀最大值就行了 一种正解是后缀数组+并查集 但据说还有后缀数组+单调栈的高端操作蒟蒻的我当然不会 后缀数组求出height,然后从大到小排 ...
- Uoj #131. 【NOI2015】品酒大会 后缀数组,并查集
#131. [NOI2015]品酒大会 统计 描述 提交 自定义测试 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项, ...
- BZOJ.4199.[NOI2015]品酒大会(后缀数组 单调栈)
BZOJ 洛谷 后缀自动机做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 显然只需要考虑极长的相同子串的贡献,然后求后缀和/后缀\(\max\)就可以了. 对于相同子串,我们能想 ...
- 【BZOJ-4199】品酒大会 后缀数组 + 并查集合并集合
4199: [Noi2015]品酒大会 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 436 Solved: 243[Submit][Status] ...
随机推荐
- Xcode8如何创建Framework静态SDK库
iOS的软件开发工具包SDK,一般是以库的形式出现,从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行.开发过程中,我们常常会用到第三方的SDK.尤其是微信.QQ.百度地图.支付功能等.其 ...
- Builder 设计模式的学习
Buileder(生成器)—对象创建型模式 一 意图 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 二 适用性 在以下情况使用Build模式: 1 当创建复杂对象的算法应 ...
- YII 用gii生成modules模块下的mvc
1.生成model ModelPath设置为: application.modules.[moduleName].models 2.生成CURD ModelClass设置为: application. ...
- Python 底层原理知识
1.Python是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一.对象的引用计数机制 Python内部使用引用计数,来保持追踪内存中的对象,所有对象都 ...
- JSON学习笔记-1
1.什么是JSON? JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMAScript (欧洲计算机协会制定的js规范)的一 ...
- 如何在微软Windows平台上打造出你的Linux开发环境(转载)
如何在微软Windows平台上打造出你的Linux开发环境 投递人 itwriter 发布于 2013-12-10 11:18 评论(1) 有348人阅读 原文链接 [收藏] « » 英文原文: ...
- 风险管理,未雨绸缪——《代码之殇》读书笔记II
这次的内容主要是关于软件开发过程中的风险管理,包括项目用时估计.产品的发布与更新.承诺兑现的重要性. ①项目用时估计 有人会质疑项目用时估计的可靠性,因为就事而言,这次的任务可能和上次不一样了,开发环 ...
- Ionic step by step (1)
刚接触 ionic,一步一步学习,有错误的,望大家指出. 公式 Ionic = Cordova + Angular2 + ionic CSS Cordova: 提供了使用 JavaScript 调用 ...
- 7.Spring MVC静态资源访问
在SpringMVC中常用的就是Controller与View.但是我们常常会需要访问静态资源,如html,js,css,image等. 默认的访问的URL都会被DispatcherServlet所拦 ...
- [TSQL|SQLSERVER|MSSQL数据库] 将数据库文件与日志附加到数据库引擎,以及转移数据库文件位置
附加: USE [master] GO CREATE DATABASE [database_name] ON ( FILENAME = N'C:\Data\<database name>. ...