题意:若两个字符开始的后面r个字符都一样,则称这两个字符是r相似的。它们也是r-1相似的。

对于r∈[0,n)分别求有多少种方案,其中权值最大方案权值是多少。此处权值是选出的两个字符的权值之积。

解:后缀自动机吊打后缀数组!!!

先看第一问,我们考虑后缀自动机上每个节点的贡献。显然cnt>1的节点才会有贡献。

它会对r ∈ len[fail[x]] + 1  ~  len[x]这一段的答案产生C(cntx,2)的贡献。这就是一个区间加法。

有个小问题,如果r减少那么相应的可选的其实会变多,但是此处我们不统计,那些会在以另一个字符结尾的别的节点上考虑到。

这样第一问就解决了。第二问?发现问题很大...一个节点的每个串都是结尾相同,开头不同。那么开头的权值之积就不好维护。

因为结尾相同,所以考虑反着建后缀自动机,然后就变成了开头相同了。那么如何维护乘积最大值呢?

考虑到一个节点的末尾所在位置,也就是它的right集合。显然就是fail树的子树中所有在主链上的节点。

于是每个点维护子树最大值即可。因为有负数所以还要最小值。

这样一个节点对第二问的贡献就是区间取max了。这两问的操作都可以用线段树搞定。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4.  
  5. typedef long long LL;
  6. const int N = ;
  7.  
  8. int tr[N][], tot = , fail[N], len[N], cnt[N], bin[N], topo[N], last = , val[N], n;
  9. int max1[N], max2[N], min1[N], min2[N];
  10. char str[N];
  11. LL tag[N << ], large[N << ];
  12.  
  13. inline void insert(char c) {
  14. int f = c - 'a';
  15. int p = last, np = ++tot;
  16. last = np;
  17. len[np] = len[p] + ;
  18. cnt[np] = ;
  19. while(p && !tr[p][f]) {
  20. tr[p][f] = np;
  21. p = fail[p];
  22. }
  23. if(!p) {
  24. fail[np] = ;
  25. }
  26. else {
  27. int Q = tr[p][f];
  28. if(len[Q] == len[p] + ) {
  29. fail[np] = Q;
  30. }
  31. else {
  32. int nQ = ++tot;
  33. len[nQ] = len[p] + ;
  34. fail[nQ] = fail[Q];
  35. fail[Q] = fail[np] = nQ;
  36. memcpy(tr[nQ], tr[Q], sizeof(tr[Q]));
  37. while(tr[p][f] == Q) {
  38. tr[p][f] = nQ;
  39. p = fail[p];
  40. }
  41. }
  42. }
  43. return;
  44. }
  45.  
  46. inline void update(int x, int y) {
  47. int t[];
  48. t[] = max1[x];
  49. t[] = max2[x];
  50. t[] = max1[y];
  51. t[] = max2[y];
  52. std::sort(t, t + );
  53. max1[x] = t[];
  54. max2[x] = t[];
  55. t[] = min1[x];
  56. t[] = min2[x];
  57. t[] = min1[y];
  58. t[] = min2[y];
  59. std::sort(t, t + );
  60. min1[x] = t[];
  61. min2[x] = t[];
  62. return;
  63. }
  64.  
  65. inline void prework() {
  66. for(int i = ; i <= tot; i++) {
  67. bin[len[i]]++;
  68. }
  69. for(int i = ; i <= tot; i++) {
  70. bin[i] += bin[i - ];
  71. }
  72. for(int i = ; i <= tot; i++) {
  73. topo[bin[len[i]]--] = i;
  74. }
  75. for(int i = tot; i >= ; i--) {
  76. int a = topo[i];
  77. cnt[fail[a]] += cnt[a];
  78. update(fail[a], a);
  79. }
  80. return;
  81. }
  82.  
  83. inline void pushdown(int o) {
  84. if(tag[o]) {
  85. tag[o << ] += tag[o];
  86. tag[o << | ] += tag[o];
  87. tag[o] = ;
  88. }
  89. large[o << ] = std::max(large[o << ], large[o]);
  90. large[o << | ] = std::max(large[o << | ], large[o]);
  91. return;
  92. }
  93.  
  94. void add(int L, int R, LL v, int l, int r, int o) {
  95. if(L <= l && r <= R) {
  96. tag[o] += v;
  97. return;
  98. }
  99. int mid = (l + r) >> ;
  100. pushdown(o);
  101. if(L <= mid) {
  102. add(L, R, v, l, mid, o << );
  103. }
  104. if(mid < R) {
  105. add(L, R, v, mid + , r, o << | );
  106. }
  107. return;
  108. }
  109.  
  110. void out(int l, int r, int o) {
  111. if(l == r) {
  112. if(r != n) {
  113. printf("%lld %lld \n", tag[o], tag[o] ? large[o] : );
  114. }
  115. return;
  116. }
  117. int mid = (l + r) >> ;
  118. pushdown(o);
  119. out(l, mid, o << );
  120. out(mid + , r, o << | );
  121. return;
  122. }
  123.  
  124. void change(int L, int R, LL v, int l, int r, int o) {
  125. if(v <= large[o]) {
  126. return;
  127. }
  128. if(L <= l && r <= R) {
  129. large[o] = v;
  130. return;
  131. }
  132. int mid = (l + r) >> ;
  133. pushdown(o);
  134. if(L <= mid) {
  135. change(L, R, v, l, mid, o << );
  136. }
  137. if(mid < R) {
  138. change(L, R, v, mid + , r, o << | );
  139. }
  140. return;
  141. }
  142.  
  143. int main() {
  144. memset(max1, ~0x3f, sizeof(max1));
  145. memset(max2, ~0x3f, sizeof(max2));
  146. memset(min1, 0x3f, sizeof(min1));
  147. memset(min2, 0x3f, sizeof(min2));
  148. memset(large, ~0x3f, sizeof(large));
  149. scanf("%d", &n);
  150. scanf("%s", str + );
  151. int l1 = -0x3f3f3f3f, l2 = -0x3f3f3f3f, s1 = 0x3f3f3f3f, s2 = 0x3f3f3f3f;
  152. for(int i = ; i <= n; i++) {
  153. scanf("%d", &val[i]);
  154. if(l1 < val[i]) {
  155. l2 = l1;
  156. l1 = val[i];
  157. }
  158. else if(l2 < val[i]) {
  159. l2 = val[i];
  160. }
  161. if(s1 > val[i]) {
  162. s2 = s1;
  163. s1 = val[i];
  164. }
  165. else if(s2 > val[i]) {
  166. s2 = val[i];
  167. }
  168. }
  169. for(int i = n; i >= ; i--) {
  170. insert(str[i]);
  171. max1[last] = min1[last] = val[i];
  172. }
  173. prework();
  174. //
  175. for(int i = ; i <= tot; i++) {
  176. if(cnt[i] < ) {
  177. continue;
  178. }
  179. // len[fail[i]] + 1 ~ len[i]
  180. add(len[fail[i]] + , len[i], 1ll * cnt[i] * (cnt[i] - ) / , , n, );
  181. change(len[fail[i]] + , len[i], std::max(1ll * max1[i] * max2[i], 1ll * min1[i] * min2[i]), , n, );
  182. }
  183.  
  184. printf("%lld %lld \n", 1ll * n * (n - ) / , std::max(1ll * l1 * l2, 1ll * s1 * s2));
  185. out(, n, );
  186. return ;
  187. }

AC代码

洛谷P2178 品酒大会的更多相关文章

  1. 洛谷P2178 品酒大会【后缀数组】【单调栈】

    题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...

  2. 洛谷 P2178 [NOI2015]品酒大会 解题报告

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

  3. 洛谷P2178 [NOI2015]品酒大会 后缀数组+单调栈

    P2178 [NOI2015]品酒大会 题目链接 https://www.luogu.org/problemnew/show/P2178 题目描述 一年一度的"幻影阁夏日品酒大会" ...

  4. 洛谷P2178 [NOI2015]品酒大会

    题目描述 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战 两个环节,分别向优胜者颁发“首席品酒家”和“首席猎手”两个奖项,吸引了众多品酒师参加. 在大会的晚餐上,调酒师 Rainb ...

  5. 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)

    题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...

  6. 【BZOJ4199】【NOI2015】品酒大会(后缀数组)

    [BZOJ4199][NOI2015]品酒大会 题面 BZOJ Uoj 洛谷 题解 考虑最裸的暴力 枚举每次的长度 以及两个开始的位置 检查以下是否满足条件,如果可以直接更新答案 复杂度\(O(n^3 ...

  7. [BZOJ]4199 品酒大会(Noi2015)

    讲道理是后缀数组裸题吧,虽然知道后缀数组的原理但是小C不会写是什么鬼.. 小C趁着做这题的当儿,学习了一下后缀数组. 网络上的后缀数组模板完全看不懂怎么破,全程照着黄学长的代码抄,感觉黄学长写得还是很 ...

  8. NOIP2017提高组Day2T2 宝藏 洛谷P3959 状压dp

    原文链接https://www.cnblogs.com/zhouzhendong/p/9261079.html 题目传送门 - 洛谷P3959 题目传送门 - Vijos P2032 题意 给定一个 ...

  9. BZOJ4199:[NOI2015]品酒大会——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4199 https://www.luogu.org/problemnew/show/P2178#su ...

随机推荐

  1. bootstrap modal垂直居中(简单封装)

    1.使用modal 弹出事件方法: 未封装前: <!DOCTYPE html> <html lang="en"> <head> <meta ...

  2. Django Rest framework 框架之认证使用和源码执行流程

    用这个框架需要先安装: pip3 install djangorestframework 如果写了一个CBV的东西,继承了View. # 继承Django里面View class APIView(Vi ...

  3. jenkins插件findbugs+pmd+checkstyle结合sonar与maven(java环境代码质量和代码规范管理)

    一.下载jdk并安装(最好jdk官网下载解压安装的) 二.下载maven并安装maven 三.安装jenkins及插件 安装checkstyle.pmd.findbugs.maven.sonar等相关 ...

  4. 浅谈基于Prism的软件系统的架构设计

    很早就想写这么一篇文章来对近几年使用Prism框架来设计软件来做一次深入的分析了,但直到最近才开始整理,说到软件系统的设计这里面有太多的学问,只有经过大量的探索才能够设计出好的软件产品,就本人的理解, ...

  5. 莫烦scikit-learn学习自修第二天【算法地图】

    1. 算法地图

  6. linux 查看网段内所有IP

    如有转载,不胜荣幸.http://www.cnblogs.com/aaron-agu/ 方法一: nmap –nsP 192.168.1.0/24 #从192.168.1.0到192.168.1.25 ...

  7. NPOI 上传Excel功能

    1.首先写一个Excel表格,第一行数据库类型(varchar.date.decimal).第二行数据库类型长度(100.12,4.时间日期为空)2.html 加按钮 { type: "bu ...

  8. Codeforces#543 div2 A. Technogoblet of Fire(阅读理解)

    题目链接:http://codeforces.com/problemset/problem/1121/A 真·阅读理解 题意就是 有n个人 pi表示他们的强度 si表示他们来自哪个学校 现在Arkad ...

  9. codeforces546C

    Soldier and Cards CodeForces - 546C Two bored soldiers are playing card war. Their card deck consist ...

  10. python----函数的动态传参

    函数的动态传参 *args 将所有的实参的位置参数聚合到一个元组,并将这个元组赋值给args 有些时候,对于函数,传入的实参数量可能是不固定的,也就是动态的,这个时候我们就需要用到函数的动态传参.下面 ...