Bzoj4199:[NOI2015]品酒大会
题面
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 = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
int a[_], n, s[_], rk[_], sa[_], height[_], tmp[_], t[_];
ll ans1[_], ans2[_];
int st[20][_], lg[_];
char ss[_];
IL int Cmp(RG int i, RG int j, RG int k){
return tmp[i] == tmp[j] && i + k <= n && j + k <= n && tmp[i + k] == tmp[j + k];
}
IL void Suffix_Sort(){
RG int m = 26;
for(RG int i = 1; i <= n; ++i) ++t[rk[i] = s[i]];
for(RG int i = 1; i <= m; ++i) t[i] += t[i - 1];
for(RG int i = n; i; --i) sa[t[rk[i]]--] = i;
for(RG int k = 1; k <= n; k <<= 1){
RG int l = 0;
for(RG int i = n - k + 1; i <= n; ++i) tmp[++l] = i;
for(RG int i = 1; i <= n; ++i) if(sa[i] > k) tmp[++l] = sa[i] - k;
for(RG int i = 0; i <= m; ++i) t[i] = 0;
for(RG int i = 1; i <= n; ++i) ++t[rk[tmp[i]]];
for(RG int i = 1; i <= m; ++i) t[i] += t[i - 1];
for(RG int i = n; i; --i) sa[t[rk[tmp[i]]]--] = tmp[i];
swap(tmp, rk), rk[sa[1]] = l = 1;
for(RG int i = 2; i <= n; ++i) rk[sa[i]] = Cmp(sa[i - 1], sa[i], k) ? l : ++l;
if(l >= n) break;
m = l;
}
for(RG int i = 1, h = 0; i <= n; ++i){
if(h) --h;
while(s[i + h] == s[sa[rk[i] - 1] + h]) ++h;
height[rk[i]] = h;
}
}
IL void ST_Prepare(){
for(RG int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
for(RG int i = 1; i <= n; ++i) st[0][i] = height[i];
for(RG int i = 1; i <= lg[n]; ++i)
for(RG int j = 1; j + (1 << i) - 1 <= n; ++j)
st[i][j] = min(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
}
IL int LCP(RG int x, RG int y){
x = rk[x], y = rk[y];
if(x > y) swap(x, y); ++x;
RG int len = y - x + 1;
return min(st[lg[len]][x], st[lg[len]][y - (1 << lg[len]) + 1]);
}
int main(RG int argc, RG char* argv[]){
Fill(ans2, -127), n = Input(), scanf(" %s", ss + 1);
for(RG int i = 1; i <= n; ++i) s[i] = ss[i] - 'a' + 1, a[i] = Input();
Suffix_Sort(), ST_Prepare();
for(RG int i = 1; i < n; ++i)
for(RG int j = i + 1; j <= n; ++j){
RG int lcp = LCP(i, j);
++ans1[0], --ans1[lcp + 1];
ans2[lcp] = max(ans2[lcp], 1LL * a[i] * a[j]);
}
for(RG int i = 0; i < n; ++i) ans1[i] += ans1[i - 1];
for(RG int i = n - 1; ~i; --i) ans2[i] = max(ans2[i], ans2[i + 1]);
for(RG int i = 0; i < n; ++i) printf("%lld %lld\n", ans1[i], ans2[i] == ans2[n] ? 0 : ans2[i]);
return 0;
}
正解,把\(height\)从大到小排序
每次合并\(height\)两边的集合,因为从大到小,所以两边集合的\(LCP\)一定就是这个\(height\),因为有负数权值,维护集合的最大值,最小值组合
# 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 = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
int a[_], n, s[_], rk[_], sa[_], height[_], tmp[_], t[_];
ll ans1[_], ans2[_];
int fa[_], id[_], size[_], mx[_], mn[_];
char ss[_];
IL int Cmp(RG int i, RG int j, RG int k){
return tmp[i] == tmp[j] && i + k <= n && j + k <= n && tmp[i + k] == tmp[j + k];
}
IL int _Cmp(RG int x, RG int y){
return height[x] > height[y];
}
IL void Suffix_Sort(){
RG int m = 26;
for(RG int i = 1; i <= n; ++i) ++t[rk[i] = s[i]];
for(RG int i = 1; i <= m; ++i) t[i] += t[i - 1];
for(RG int i = n; i; --i) sa[t[rk[i]]--] = i;
for(RG int k = 1; k <= n; k <<= 1){
RG int l = 0;
for(RG int i = n - k + 1; i <= n; ++i) tmp[++l] = i;
for(RG int i = 1; i <= n; ++i) if(sa[i] > k) tmp[++l] = sa[i] - k;
for(RG int i = 0; i <= m; ++i) t[i] = 0;
for(RG int i = 1; i <= n; ++i) ++t[rk[tmp[i]]];
for(RG int i = 1; i <= m; ++i) t[i] += t[i - 1];
for(RG int i = n; i; --i) sa[t[rk[tmp[i]]]--] = tmp[i];
swap(tmp, rk), rk[sa[1]] = l = 1;
for(RG int i = 2; i <= n; ++i) rk[sa[i]] = Cmp(sa[i - 1], sa[i], k) ? l : ++l;
if(l >= n) break;
m = l;
}
for(RG int i = 1, h = 0; i <= n; ++i){
if(h) --h;
while(s[i + h] == s[sa[rk[i] - 1] + h]) ++h;
height[rk[i]] = h;
}
}
IL int Find(RG int x){
return fa[x] == x ? x : fa[x] = Find(fa[x]);
}
int main(RG int argc, RG char* argv[]){
Fill(ans2, -127), n = Input(), scanf(" %s", ss + 1);
for(RG int i = 1; i <= n; ++i) s[i] = ss[i] - 'a' + 1, a[i] = Input();
for(RG int i = 1; i <= n; ++i) fa[i] = id[i] = i, size[i] = 1, mx[i] = mn[i] = a[i];
Suffix_Sort(), sort(id + 2, id + n + 1, _Cmp);
for(RG int i = 2; i <= n; ++i){
RG int fx = Find(sa[id[i] - 1]), fy = Find(sa[id[i]]);
ans1[height[id[i]]] += 1LL * size[fx] * size[fy];
fa[fy] = fx, size[fx] += size[fy];
ans2[height[id[i]]] = max(ans2[height[id[i]]], max(1LL * mx[fx] * mx[fy], 1LL * mx[fx] * mn[fy]));
ans2[height[id[i]]] = max(ans2[height[id[i]]], max(1LL * mn[fx] * mx[fy], 1LL * mn[fx] * mn[fy]));
mx[fx] = max(mx[fx], mx[fy]);
mn[fx] = min(mn[fx], mn[fy]);
}
for(RG int i = n - 1; ~i; --i) ans1[i] += ans1[i + 1], ans2[i] = max(ans2[i], ans2[i + 1]);
for(RG int i = 0; i < n; ++i) printf("%lld %lld\n", ans1[i], ans2[i] == ans2[n] ? 0 : ans2[i]);
return 0;
}
Bzoj4199:[NOI2015]品酒大会的更多相关文章
- [UOJ#131][BZOJ4199][NOI2015]品酒大会 后缀数组 + 并查集
[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...
- [UOJ#131][BZOJ4199][NOI2015]品酒大会
[UOJ#131][BZOJ4199][NOI2015]品酒大会 试题描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个 ...
- [bzoj4199][Noi2015]品酒大会_后缀自动机_后缀树_树形dp
品酒大会 bzoj-4199 Noi-2015 题目大意:给定一个字符串,如果其两个子串的前$r$个字符相等,那么称这两个子串的开头两个位置$r$相似.如果两个位置勾兑在一起那么美味度为两个位置的乘积 ...
- [BZOJ4199][NOI2015]品酒大会
#131. [NOI2015]品酒大会 统计 描述 提交 自定义测试 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项, ...
- bzoj4199: [Noi2015]品酒大会(后缀数组)
题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...
- BZOJ4199 [Noi2015]品酒大会 【后缀数组 + 单调栈 + ST表】
题目 一年一度的"幻影阁夏日品酒大会"隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发"首席品 酒家"和"首席猎手"两个奖项,吸 ...
- 并不对劲的bzoj4199: [Noi2015]品酒大会
传送门-> 又称普及大会. 这题没什么好说的……后缀自动机裸题……并不对劲的人太菜了,之前照着标程逐行比对才过了这道题,前几天刚刚把这题一遍写对…… 这题的输出和某两点相同后缀的长度有关,那么把 ...
- 2019.02.28 bzoj4199: [Noi2015]品酒大会(sam+线段树)
传送门 题意:给一个串,每个位置有一个权值,当S[s...s+len−1]=S[t...t+len−1]&&S[s...s+len]̸=S[t..t+len]S[s...s+len-1 ...
- bzoj千题计划257:bzoj4199: [Noi2015]品酒大会
http://www.lydsy.com/JudgeOnline/problem.php?id=4199 求出后缀数组的height 从大到小枚举,合并 维护组内 元素个数,最大.次大.最小.次小 # ...
- [BZOJ4199][Noi2015]品酒大会 树形DP+后缀自动机
由于要找后缀的前缀,所以先用反串建立SAM. link边组成了后缀树. 两个子串的最长公共前缀是LCA的step 树形dp即可. #include<iostream> #include&l ...
随机推荐
- Linux常用命令手册
Linux常用命令手册 NO 分类 PS1 命令名 用法及参数 功能注解 对应章节 1 文件管理 # ls ls -a 列出当前目录下的所有文件,包括以.头的隐含文件 文件管理 # ls ls ...
- 编写一个js函数,该函数有一个n(数字类型),其返回值是一个数组,该数组内是n个随机且不重复的整数,且整数取值范围是[2,32]
首先定义个fn用来返回整数的取值范围: function getRand(a,b){ var rand = Math.ceil(Math.random()*(b-a)+a); return rand; ...
- java11 - GUI图形用户界面编程
一.JavaSwing界面设计参考教程 二.布局管理器 三.绝对布局 import java.awt.Color; import java.awt.event.ActionEvent; import ...
- Spring data mongodb 聚合,投射,内嵌数组文档分页.
尽量别直接用 DBObject ,Spring data mongodb 的api 本来就没什么多大用处,如果还直接用 DBObject 那么还需要自己去解析结果,说动做个对象映射,累不累 Spri ...
- C语言老司机学Python (五)
今天看的是标准库概览. 操作系统接口: 用os模块实现. 针对文件和目录管理,还有个shutil模块可以用. 例句: import os os.getcwd() # 返回当前的工作目录 os.chdi ...
- shell脚本基础 循环机构
循环结构 for循环格式一格式:for 变量 in 值1 值2 ........(值不一定是数字,可以是命令或者其他的)do 命令done [root@ceshiji ~]# vim a.sh #!/ ...
- 判断二叉树是否二叉排序树(BST)
算法思想:由于二叉排序树的中序遍历可以得到一个有序的序列,因此,我们可以使用中序遍历进行求解. 代码如下: #include <stack> using namespace std; ty ...
- 利用Caffe训练模型(solver、deploy、train_val)+python使用已训练模型
本文部分内容来源于CDA深度学习实战课堂,由唐宇迪老师授课 如果你企图用CPU来训练模型,那么你就疯了- 训练模型中,最耗时的因素是图像大小size,一般227*227用CPU来训练的话,训练1万次可 ...
- Hi3531用SPI FLASH启动 使用Nand做文件系统
1.编译内核(可选) make ARCH=arm CROSS_COMPILE=arm-hisiv200-linux- menuconfig make ARCH=arm CROSS_COMPILE=ar ...
- yii学习笔记--使用gii快速创建控制器和模型
配置gii 'gii'=>array( 'class'=>'system.gii.GiiModule', 'password'=>'123456',//使用gii的密码 // If ...