题意

题目链接

Sol

说一个后缀自动机+线段树的无脑做法

首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值

显然一个串能更新答案的区间是\([len_{fa_{x}} + 1, len_x]\),方案数就相当于是从\(siz_x\)里面选两个,也就是\(\frac{siz_x (siz_x - 1)}{2}\)

直接拿线段树维护一下,标记永久化一下炒鸡好写~

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. #define LL long long
  4. using namespace std;
  5. const int MAXN = 1e6 + 10;
  6. const LL INF = 2e18 + 10;
  7. template <typename A, typename B> inline bool chmin(A &a, B b){if(a > b) {a = b; return 1;} return 0;}
  8. template <typename A, typename B> inline bool chmax(A &a, B b){if(a < b) {a = b; return 1;} return 0;}
  9. inline int read() {
  10. char c = getchar(); int x = 0, f = 1;
  11. while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
  12. while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  13. return x * f;
  14. }
  15. int N, a[MAXN];
  16. char s[MAXN];
  17. int root = 1, tot = 1, las = 1, ch[MAXN][26], fa[MAXN], len[MAXN], rev[MAXN];
  18. LL mx[MAXN], mx2[MAXN], ans[MAXN], ans1[MAXN], mn[MAXN], mn2[MAXN], tmp[MAXN], siz[MAXN];
  19. vector<int> v[MAXN];
  20. void insert(int x, int id) {
  21. int now = ++tot, pre = las; las = now; siz[now] = 1; len[now] = len[pre] + 1; mx[now] = a[id]; mx2[now] = -INF; mn[now] = a[id]; mn2[now] = INF; rev[id] = now;
  22. for(; pre && !ch[pre][x]; pre = fa[pre]) ch[pre][x] = now;
  23. if(!pre) {fa[now] = root; return ;}
  24. int q = ch[pre][x];
  25. if(len[pre] + 1 == len[q]) fa[now] = q;
  26. else {
  27. int nq = ++tot; fa[nq] = fa[q]; len[nq] = len[pre] + 1;
  28. memcpy(ch[nq], ch[q], sizeof(ch[q]));
  29. fa[now] = fa[q] = nq;
  30. for(; pre && ch[pre][x] == q; pre = fa[pre]) ch[pre][x] = nq;
  31. }
  32. }
  33. void BuildDAG() {
  34. for(int i = 1; i <= tot; i++) assert(fa[i] != i), v[fa[i]].push_back(i);
  35. }
  36. int rt, Node, ls[MAXN], rs[MAXN], ad[MAXN], si[MAXN];
  37. LL sum[MAXN], tag[MAXN];
  38. void Build(int &k, int l, int r) {
  39. if(!k) k = ++Node, tag[k] = -INF, si[k] = r - l + 1;
  40. if(l == r) return ;
  41. int mid = l + r >> 1;
  42. Build(ls[k], l, mid);
  43. Build(rs[k], mid + 1, r);
  44. }
  45. void IntMax(int k, int l, int r, int ql, int qr, LL v) {
  46. if(ql <= l && r <= qr) {chmax(tag[k], v); return ; }
  47. int mid = l + r >> 1;
  48. if(ql <= mid) IntMax(ls[k], l, mid, ql, qr, v);
  49. if(qr > mid) IntMax(rs[k], mid + 1, r, ql, qr, v);
  50. }
  51. void IntAdd(int k, int l, int r, int ql, int qr, LL v) {
  52. if(ql <= l && r <= qr) {sum[k] += v; return ;}
  53. int mid = l + r >> 1;
  54. if(ql <= mid) IntAdd(ls[k], l, mid, ql, qr, v);
  55. if(qr > mid) IntAdd(rs[k], mid + 1, r, ql, qr, v);
  56. }
  57. LL QueryNum(int k, int l, int r, int pos) {
  58. if(!k) return 0;
  59. LL now = sum[k];
  60. if(l == r || !k) return now;
  61. int mid = l + r >> 1;
  62. if(pos <= mid) now += QueryNum(ls[k], l, mid, pos);
  63. else now += QueryNum(rs[k], mid + 1, r, pos);
  64. return now;
  65. }
  66. LL QueryMax(int k, int l, int r, int pos) {
  67. if(!k) return -INF;
  68. LL now = tag[k];
  69. if(l == r || !k) return now;
  70. int mid = l + r >> 1;
  71. if(pos <= mid) chmax(now, QueryMax(ls[k], l, mid, pos));
  72. else chmax(now, QueryMax(rs[k], mid + 1, r, pos));
  73. return now;
  74. }
  75. void dfs(int x) {
  76. for(auto &to : v[x]) {
  77. dfs(to);
  78. siz[x] += siz[to];
  79. if(mx2[to] > mx[x]) chmax(mx2[x], mx[x]), mx[x] = mx2[to];
  80. else chmax(mx2[x], mx2[to]);
  81. if(mx[to] > mx[x]) chmax(mx2[x], mx[x]), mx[x] = mx[to];
  82. else chmax(mx2[x], mx[to]);
  83. if(mn2[to] < mn[x]) chmin(mn2[x], mn[x]), mn[x] = mn2[to];
  84. else chmin(mn2[x], mn2[to]);
  85. if(mn[to] < mn[x]) chmin(mn2[x], mn[x]), mn[x] = mn[to];
  86. else chmin(mn2[x], mn[to]);
  87. }
  88. if(siz[x] > 1 && x != root) {
  89. IntMax(rt, 1, N, len[fa[x]] + 1, len[x], mx[x] * mx2[x]);
  90. IntMax(rt, 1, N, len[fa[x]] + 1, len[x], mn[x] * mn2[x]);
  91. IntAdd(rt, 1, N, len[fa[x]] + 1, len[x], 1ll * siz[x] * (siz[x] - 1) / 2);
  92. }
  93. }
  94. signed main() {
  95. N = read();
  96. Build(rt, 1, N);
  97. scanf("%s", s + 1);
  98. reverse(s + 1, s + N + 1);
  99. for(int i = 1; i <= N; i++) tmp[i] = a[i] = read(), assert(a[i] != 0);
  100. reverse(a + 1, a + N + 1);
  101. for(int i = 1; i <= N; i++) insert(s[i] - 'a', i);
  102. for(int i = 1; i <= tot; i++) {
  103. ans[i] = -INF;
  104. if(!mx[i]) mx[i] = -INF;
  105. if(!mx2[i]) mx2[i] = -INF;
  106. if(!mn[i]) mn[i] = INF;
  107. if(!mn2[i]) mn2[i] = INF;
  108. }
  109. BuildDAG();
  110. dfs(1);
  111. for(int i = 1; i < N; i++) {
  112. ans1[i] = QueryNum(root, 1, N, i);
  113. ans[i] = QueryMax(root, 1, N, i);
  114. }
  115. sort(tmp + 1, tmp + N + 1, greater<int>());
  116. cout << 1ll * N * (N - 1) / 2 << " " << max(tmp[1] * tmp[2], tmp[N] * tmp[N - 1]) << '\n';
  117. for(int i = 1; i < N; i++) cout << ans1[i] << " " << (ans[i] <= -INF ? 0 : ans[i]) << '\n';
  118. return 0;
  119. }
  120. /*
  121. 2
  122. aa
  123. -100000000 100000000
  124. 12
  125. abaabaabaaba
  126. 1 -2 3 -4 5 -6 7 -8 9 -10 11 -12
  127. */

洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)的更多相关文章

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

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

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

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

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

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

  4. BZOJ.4199.[NOI2015]品酒大会(后缀自动机 树形DP)

    BZOJ 洛谷 后缀数组做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 只考虑求极长相同子串,即所有后缀之间的LCP. 而后缀的LCP在后缀树的LCA处.同差异这道题,在每个点处 ...

  5. 【BZOJ 4199】[Noi2015]品酒大会 后缀自动机+DP

    题意 两个长度为$r$的子串相等称为$r$相似,两个$r$相似的权值等于子串开头位置权值乘积,给定字符串和每个位置权值,求$r$相似子串数量和最大权值乘积 对反串建立后缀自动机得到后缀树,后缀树上两个 ...

  6. 【bzoj4199】[Noi2015]品酒大会 后缀自动机求后缀树+树形dp

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

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

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

  8. 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 ...

  9. BZOJ 4199: [Noi2015]品酒大会 后缀自动机_逆序更新

    一道裸题,可以考虑自底向上去更新方案数与最大值. 没啥难的 细节........ Code: #include <cstdio> #include <algorithm> #i ...

随机推荐

  1. faster-RCNN台标检测

    最近学习了faster-RCNN算法,收获不少,记此文为证.faster-RCNN是一个目标检测算法,它能够识别多个目标,对目标分类并标注位置,非常好用.它的输入样本是标注好的图片,输出是一个hdf5 ...

  2. 机器学习之KNN算法

    1 KNN算法 1.1 KNN算法简介 KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属 ...

  3. 初涉Java方法

    初涉Java方法 方法就是一段可重复调用的代码段,方法命名规范,第一个单词小写,从第二个单词开始首字母均大写.         格式:              public static 返回值类型 ...

  4. 为什么我们喜欢用 sigmoid 这类 S 型非线性变换?

    本文整理自 @老师木 的一条图片新浪微博,从另一个角度给出为何采用 sigmoid 函数作非线性变换的解释. 为什么我们喜欢用 sigmoid 这类 S 型非线性变换?

  5. Intellij-忽略其他编译错误,运行当前文件

    在使用Intellij IDEA的时候,有时候想在项目中写个main()方法或者单元测试测试个功能,但是有其他文件编译出错IDEA就提示我说不能运行,很是烦恼. 能不能像Eclipse一样呢,其他编译 ...

  6. Windows抓屏技术

    Windows桌面共享中一些常见的抓屏技术 1. BitBlt   我想做Windows开发应该都知道这个API, 它能实现DC间的内容拷贝, 如果我们把源DC指定成Monitor DC或是桌面DC, ...

  7. Linux_CentOS-服务器搭建 <一>

    本人CentOS版本6.3 必备的两个小软件: 安装PUTTY远程控制linux的非常小但非常好用的小工具. 安装WINSCP,使用ssh实现我windows上和linux服务器上文件的互传. 呵呵, ...

  8. 从零开始学 Web 之 jQuery(六)为元素绑定多个相同事件,解绑事件

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  9. 面试:C++实现访问者模式

    参考:深入应用C++11,访问者模式 #include <iostream> class ConcreteElement1; class ConcreteElement2; class V ...

  10. C语言第九讲,结构体

    C语言第九讲,结构体 一丶结构体的定义 在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据.结构体的定义形式为: struct 结构体名{ 结构体所包含的变量或数组 }; 结构体是一种 ...