题目传送门

题意:

调酒师  调制了  杯鸡尾酒。这  杯鸡尾酒排成一行,其中第  杯酒被贴上了一个标签  ,每杯酒有一个美味值,每个标签都是  个小写英文字母之一,把这些酒想象为一个字符串  。两个长度都是  的子序列,子序列起始位置分别是 。如果这两个子序列相等,那就认为是“ 相似”。这两个子序列的美味值是  。

问你“ 相似”的种类数和最大美味值。 。

题解:

首先众所周知:

需要把  想象成 连接  和  的边。这个很重要,想到这里就会了。

掏出后缀数组板子,我们就有了  数组。建议使用结构体,存储权值和连接的点。

然后我们按权值从大到小排列  数组。

逐渐加边,然后维护所需信息就好了。

说一下怎么维护信息:

第一个答案是多少种方法可以选出2杯“ r 相似”的酒。这道题比较特殊,因为原图是一条链,也就是一棵树,所以每次加边后就是把两个连通块连接成一个连通块,这样通过乘法原理,加边时加上两个连通块大小的乘积就好了。

第二个答案是选择 2 杯“r相似”的酒调兑可以得到的美味度的最大值。假如每杯酒的权值都是非负的,那么只需要两个连通块合并后的最大值乘次大值就好了。现在有负权值,我们还需要考虑最小值和次小值,因为负负得正。

每次加边时,只对当前“r相似”维护信息即可,不用考虑“[0,r-1]相似”,最后扫一遍考虑后缀就好了。

感受:

昨天一直硬怼主席树套线段树,写了200多行,自己感觉写不出来,放弃了。

看了正解,好简单。

我真菜。

这个思路很巧妙,而且感觉很自然,很好写。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 3e5 + 5 ;
int rk[maxn << 1] , sa[maxn << 1] , height[maxn << 1] ;
int tmp[maxn << 1] , cnt[maxn] ;
int n , a[maxn] ;
char s[maxn] ;
ll ans1[maxn] , ans2[maxn] ;
int pre[maxn] , f[maxn][4] ;
int siz[maxn] ;
struct node
{int x , id ;bool operator < (const node &s) const{if(x != s.x)  return x > s.x ;else  return id < s.id ;}
} h[maxn] ;
void suffixarray(int n , int m)
{n ++ ;for(int i = 0 ; i < n * 2 + 5 ; i ++)rk[i] = sa[i] = height[i] = tmp[i] = 0 ;//开2 倍空间for(int i = 0 ; i < m ; i ++)  cnt[i] = 0 ;for(int i = 0 ; i < n ; i ++)  cnt[rk[i] = s[i]] ++ ;for(int i = 1 ; i < m ; i ++)  cnt[i] += cnt[i - 1] ;for(int i = 0 ; i < n ; i ++)  sa[-- cnt[rk[i]]] = i ;for(int k = 1 ; k <= n ; k <<= 1){int j = 0 ;for(int i = 0 ; i < n ; i ++){j = sa[i] - k ;if(j < 0)  j += n ;tmp[cnt[rk[j]] ++] = j ;}sa[tmp[cnt[0] = 0]] = j = 0 ;for(int i = 1 ; i < n ; i ++){if(rk[tmp[i]] != rk[tmp[i - 1]]|| rk[tmp[i] + k] != rk[tmp[i - 1] + k])cnt[++ j] = i ;sa[tmp[i]] = j ;}memcpy(rk , sa , n * sizeof(int)) ;memcpy(sa , tmp , n * sizeof(int)) ;if(j >= n - 1)  break ;}height[0] = 0 ;for(int i = 0 , k = 0 , j = rk[0] ; i < n - 1 ; i ++ , k ++)while(~k && s[i] != s[sa[j - 1] + k])height[j] = k -- , j = rk[sa[j] + 1] ;
}
int find(int u)
{if(pre[u] == u)  return u ;return pre[u] = find(pre[u]) ;
}
void join(int k , int x , int y)
{int fx = find(x) ;int fy = find(y) ;pre[fx] = fy ;ans1[k] += ll(siz[fx]) * siz[fy] ;if(f[fx][0] > f[fy][0])f[fy][1] = f[fy][0] , f[fy][0] = f[fx][0] ;elsef[fy][1] = max(f[fy][1] , f[fx][0]) ;if(f[fx][3] < f[fy][3])f[fy][2] = f[fy][3] , f[fy][3] = f[fx][3] ;elsef[fy][2] = min(f[fy][2] , f[fx][3]) ;ll c = max(ll(f[fy][0]) * f[fy][1] , ll(f[fy][2]) * f[fy][3]) ;ans2[k] = max(ans2[k] , c) ;siz[fy] += siz[fx] ;
}
int main()
{scanf("%d" , &n) ;scanf("%s" , s) ;for(int i = 0 ; i < n ; i ++)  scanf("%d" , &a[i]) ;suffixarray(n , 200) ;for(int i = 2 ; i <= n ; i ++)h[i].x = height[i] , h[i].id = i ;sort(h + 2 , h + n + 1) ;for(int i = 0 ; i < n ; i ++)  {pre[i] = i ;siz[i] = 1 ; f[i][0] = a[i] ;f[i][1] = -2e9 ;f[i][2] = 2e9 ;f[i][3] = a[i] ;}  for(int i = 0 ; i <= n ; i ++)ans2[i] = -1e18 ;for(int i = 2 ; i <= n ; i ++)join(h[i].x , sa[h[i].id - 1] , sa[h[i].id]) ;for(int i = n - 1 ; i >= 0 ; i --){ans1[i] += ans1[i + 1] ;ans2[i] = max(ans2[i] , ans2[i + 1]) ; }for(int i = 0 ; i <= n - 1 ; i ++){if(ans1[i] == 0)  printf("0 0\n") ;elseprintf("%lld %lld\n" , ans1[i] , ans2[i]) ;    }return 0 ;
}

P2178 后缀数组 + 并查集相关推荐

  1. 【BZOJ4199】品酒大会,后缀数组+并查集维护

    Time:2016.05.23 Author:xiaoyimi 转载注明出处谢谢 传送门 题面及样例 思路: 求lcp相关问题上有类似于差异的地方,但不完全是,"差异"所求的是lc ...

  2. 花神游历各国 题解(小清新线段树/树状数组+并查集)

    题面 众所周知,这是一道小清新线段树 然而可以用树状数组水过去且跑得飞快 看到区间开方第一反应肯定是线段树懒标记区间修改之类的,但是这个东西似乎确凿不可维护 所以考虑暴力循环单点修改->T飞 于 ...

  3. hdu 6200 mustedge mustedge mustedge(dfs序+树状数组+并查集)

    题目链接:hdu 6200 mustedge mustedge mustedge 题意: 一开始给你一个有n个节点m条无向边的图,现在定义mustedge为u->v的路径上必须经过的边. 现在有 ...

  4. POJ-2985(树状数组 + 并查集 + 二分)

    题目:http://poj.org/problem?id=2985 这题考察了两个点呢,组合并用到了并查集,求第K大的数用到了树状数组,刚开始WA了几次,发现是在组合并之后仅仅update(*, -1 ...

  5. sa后缀数组使用合集,包括height数组求LPC和LCS,ST表,单调队列优化。

    P5546 [POI2000]公共串 所有串合在一起,每两个串放不同的字符,求一遍后缀数组,然后利用height数组求LCS即可. #include<iostream> #include& ...

  6. [蓝桥杯][2019年第十届真题]修改数组(并查集)

    题目描述 给定一个长度为 N 的数组 A = [A1, A2, · · · AN ],数组中有可能有重复出现 的整数. 现在小明要按以下方法将其修改为没有重复整数的数组.小明会依次修改 A2,A3,· ...

  7. [蓝桥杯2019初赛]修改数组-并查集

    代码如下: #include <iostream> using namespace std; const int N = 1000010; int a[N];int find(int x) ...

  8. 算法:程序设计之并查集

    前言 之前一直都是听说并查集,感觉是一个神乎其技,狂拽酷炫.却没有想过在自己学习并查集之前,自已在解决问题的时候也能够想到一个和并查集异曲同工的方法.这个还是很愉快的. 版权说明 著作权归作者所有. ...

  9. P2178 [NOI2015] 品酒大会(并查集+后缀数组)

    LINK T1\color{Red}{T1}T1 第ppp杯酒和第qqq杯酒的最大相似度就是LCP(suffix(p),suffix(q))LCP(suffix(p),suffix(q))LCP(su ...

最新文章

  1. 渗透测试:正义黑客的渗透测试以及该过程和黑客活动的区别
  2. flume 1.7在windows下的安装与运行
  3. 软件测试入门三年经验
  4. 从零开始搭建Android框架系列
  5. 笔记-信息化与系统集成技术-云计算操作系统的主要关键技术
  6. linux应用程序逆向,Linux下查看并下载命令源码包(根据命令/应用程序逆向获取并且安装其所属源码包)...
  7. 【华为云技术分享】《跟唐老师学习云网络》 - 我的网络概念
  8. iOS不同版本适配问题
  9. String、StringBuffer、StringBuilder有什么区别
  10. C语言选择题(含答案)
  11. 初中中考计算机考试,中考信息技术考试
  12. ClickHouse安装与引擎
  13. 矩阵连乘问题C++实现
  14. python数字转换为大写中文_阿拉伯数字金额转中文大写 (python实现)
  15. Sourcetree git status失败 错误代码128:error :bad signature 0x0000000
  16. 基于C4D的3d设计
  17. Cutting Chains UVA - 818
  18. 中国软件:10个人20年坎坷路
  19. GIT科普系列5:index in git
  20. ACM--几何––圆––角速度--HDOJ 1593--find a way to escape

热门文章

  1. 在MDK 中忽略(suppress) 某一个警告
  2. word2010中“不包含”、“不存在”等特殊字符的输入方法
  3. 拍照时怎样摆姿势好看?
  4. 王道数据结构线性表:有读者认为直接去掉p结点会造成断链?
  5. mysql多数据源配置
  6. Spring Boot Actuator 端点启用和暴露
  7. 2020年GitHub上50个最受程序员欢迎的PHP开源项目
  8. 怎样让你的LinkedIn动态上热门?高效使用我们的领英
  9. 黑马程序员---java算法实现输出任意奇数维数独
  10. Linux中awk后面的RS, ORS, FS, OFS 用法