@description@

给定 L,连续至少 L 个相同的数 k 可以合并成 1 个 k+1。

给定一个长度为 N 的序列,问该序列有多少个子区间可以通过若干次合并变成 1 个数。

Constraints

1≤N≤2×10^5, 2≤L≤N, 1≤Ai≤10^9

Input

输入形式如下:

N L

A1 A2 ... AN

Output

输出满足条件的子区间个数。

Sample Input 1

9 3

2 1 1 1 1 1 1 2 3

Sample Output 1

22

@solution@

其实思路挺简单的。

先考虑判定一个区间是否合法。

假如区间只包含一个数,显然合法。

假如区间只包含一种数,只有当这种数的个数 >= L 才合法。

否则,我们可以将最小的 x 尽可能多地合并成 x + 1。如果有不能够合并的 x 则不合法;否则递归判断新的序列是否合法。

对于不同的区间,将最小的 x 合并成 x + 1 其实可以同时做。

于是我们区间统计的方法就出来了:

先对于最小的 x,统计只包含 x 的合法区间数。

然后将所有只包含 x 的极长区间(即无法在往左右延伸的区间)合并出尽量多的 x + 1。

因为合并出来的一个数代表原序列中的一段数,所以我们还需要维护 lf 与 rf,分别表示 “一个数充当左端点的方案数” 与 “一个数充当右端点的方案数”。

具体怎么维护 lf 与 rf 呢?

对于一个 x 的极长区间 a1, a2, ..., ak。a[1...L-1] 显然无法充当右端点(无法合成出 x + 1),a[L...2L-1] 可以充当第一个 x + 1 的右端点,将 a[L...2L-1] 对应的 rf 加起来就可以得到新的第一个 x + 1 的 rf 了。

同理,a[2L...3L-1] 对应第二个 x + 1 的右端点,a[3L...4L-1] 对应第三个 x + 1 的右端……

右端点都可以求出来了,左端点就同理了。

如果一个极长区间 < L,直接将它删除。

因为可能 x 合成出来的 x + 1 还可以再合成,此时方案数会计算重复(而且会计算到不合法方案)。需要在 x 合成 x + 1 的过程中,减去 x + 1 合成 x + 2 产生的贡献,才能保证不重复。

用链表实现删减。用优先队列取出最小元素。

因为一次合并将 L 个合成 1 个,至少减少了 L-1 个元素。那么只会有 O(n) 次合并。

所以复杂度瓶颈在优先队列,为 O(nlogn)。

@accepted code@

  1. #include<cstdio>
  2. #include<queue>
  3. #include<vector>
  4. #include<iostream>
  5. using namespace std;
  6. #define mp make_pair
  7. #define fi first
  8. #define se second
  9. const int MAXN = 200000;
  10. typedef pair<int, int> pii;
  11. typedef long long ll;
  12. priority_queue<pii, vector<pii>, greater<pii> >que;
  13. int lst[MAXN + 5], nxt[MAXN + 5], N, L;
  14. void link(int x, int y) {lst[y] = x, nxt[x] = y;}
  15. bool check(int x, int y) {return nxt[x] == y;}
  16. ll lf[MAXN + 5], rf[MAXN + 5], f1[MAXN + 5], f2[MAXN + 5];
  17. vector<int>v1, v2;
  18. ll solve(int x) {
  19. ll ret = 0, tmp = 0;
  20. int lt = v2.size(), lb = lst[v2[0]], rb = nxt[v2[lt-1]];
  21. for(int i=0;i<lt;i++) {
  22. if( i - L + 1 >= 0 ) tmp += lf[v2[i-L+1]];
  23. ret += tmp * rf[v2[i]];
  24. }
  25. for(int i=0;i<lt;i++)
  26. f1[v2[i]] = lf[v2[i]], f2[v2[i]] = rf[v2[i]], lf[v2[i]] = rf[v2[i]] = 0;
  27. int c = lt / L;
  28. if( c ) {
  29. for(int i=L-1;i<lt;i++) {
  30. int t = (i + 1)/L - 1;
  31. rf[v2[t]] += f2[v2[i]];
  32. }
  33. for(int i=lt-L;i>=0;i--) {
  34. int t = c - (lt - i)/L;
  35. lf[v2[t]] += f1[v2[i]];
  36. }
  37. for(int i=1;i<c;i++)
  38. link(v2[i-1], v2[i]);
  39. link(lb, v2[0]), link(v2[c-1], rb);
  40. for(int i=0;i<c;i++)
  41. que.push(mp(x + 1, v2[i]));
  42. tmp = 0;
  43. for(int i=0;i<c;i++) {
  44. if( i - L + 1 >= 0 ) tmp += lf[v2[i-L+1]];
  45. ret -= tmp * rf[v2[i]];
  46. }
  47. }
  48. else nxt[lb] = N + 1, lst[rb] = 0;
  49. v2.clear();
  50. return ret;
  51. }
  52. int main() {
  53. scanf("%d%d", &N, &L);
  54. for(int i=1;i<=N;i++) {
  55. int x; scanf("%d", &x);
  56. que.push(mp(x, i)), link(i, i + 1);
  57. lf[i] = rf[i] = 1;
  58. }
  59. ll ans = 0; link(0, 1);
  60. while( !que.empty() ) {
  61. int x = que.top().fi; v1.clear();
  62. while( !que.empty() && que.top().fi == x )
  63. v1.push_back(que.top().se), que.pop();
  64. v2.clear(); v2.push_back(v1[0]);
  65. for(int i=1;i<v1.size();i++) {
  66. if( !check(v1[i-1], v1[i]) )
  67. ans += solve(x);
  68. v2.push_back(v1[i]);
  69. }
  70. ans += solve(x);
  71. }
  72. printf("%lld\n", ans + N);
  73. }

@details@

为什么会有一种AGC的F题变水了的错觉

即使想到了这个方向,也很难说能够把所有细节想清楚吧。

嗯。应该是这样的。

@atcoder - AGC037F@ Counting of Subarrays的更多相关文章

  1. AtCoder Grand Contest 037

    Preface 这篇咕了可能快一个月了吧,正好今天晚上不想做题就来补博客 现在还不去复习初赛我感觉我还是挺刚的(微笑) A - Dividing a String 考虑最好情况把每个字符串当作一个来看 ...

  2. AtCoder Grand Contest 037 简要题解

    从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...

  3. @atcoder - CODE FESTIVAL 2017 Elimination Tournament Round 3 F@ Unicyclic Graph Counting

    目录 @description@ @solution@ @accpeted code@ @details@ @description@ 求有多少 n 点 n 边的无向连通图,满足第 i 个点的度数为 ...

  4. AtCoder Grand Contest 005

    AtCoder Grand Contest 005 A - STring 翻译 给定一个只包含\(ST\)的字符串,如果出现了连续的\(ST\),就把他删去,然后所有位置前移.问最后剩下的串长. 题解 ...

  5. AtCoder Grand Contest 023 A - Zero-Sum Ranges

    Time limit : 2sec / Memory limit : 256MB Score : 200 points Problem Statement We have an integer seq ...

  6. Atcoder/Topcoder 口胡记录

    Atcoder/Topcoder 理论 AC Atcoder的❌游戏示范 兴致勃勃地打开一场 AGC 看 A 题,先 WA 一发,然后花了一年时间 Fix. 看 B 题,啥玩意?这能求? 睡觉觉. e ...

  7. 萌新笔记——Cardinality Estimation算法学习(二)(Linear Counting算法、最大似然估计(MLE))

    在上篇,我了解了基数的基本概念,现在进入Linear Counting算法的学习. 理解颇浅,还请大神指点! http://blog.codinglabs.org/articles/algorithm ...

  8. POJ_2386 Lake Counting (dfs 错了一个负号找了一上午)

    来之不易的2017第一发ac http://poj.org/problem?id=2386 Lake Counting Time Limit: 1000MS   Memory Limit: 65536 ...

  9. ZOJ3944 People Counting ZOJ3939 The Lucky Week (模拟)

    ZOJ3944 People Counting ZOJ3939 The Lucky Week 1.PeopleConting 题意:照片上有很多个人,用矩阵里的字符表示.一个人如下: .O. /|\ ...

随机推荐

  1. 了解真实的『REM』多终端屏幕适配

    作者:hbxeagle 链接:github.com/hbxeagle/rem/blob/master/README.md rem 作为一个低调的长度单位,由于手机端网页的兴起,在屏幕适配中得到重用.使 ...

  2. Python执行时间的计算方法小结

    Python执行时间的计算方法小结 首先说一下我遇到的坑,生产上遇到的问题,我调度Python脚本执行并监控这个进程,python脚本运行时间远远大于python脚本中自己统计的程序执行时间. 监控p ...

  3. 【JZOJ5363】【NOIP2017提高A组模拟9.14】生命之树 Trie+启发式合并

    题面 45 在比赛中,我只想到了45分的暴力. 对于一个树中点对,相当于在他们的LCA及其祖先加上这个点对的贡献. 那么这个可以用dfs序+树状数组来维护. 100 想法 我想到了可能要用trie树来 ...

  4. mysql 分表-横向,纵向

    mysql 分库分表 分表是分散数据库压力的好方法. 分表,最直白的意思,就是将一个表结构分为多个表,然后,可以再同一个库里,也可以放到不同的库. 当然,首先要知道什么情况下,才需要分表.个人觉得单表 ...

  5. pstree进程管理

    功能:pstree命令列出当前的进程,以及它们的树状结构. 格式:pstree [选项] [pid|user] 主要选项如下: -a:显示执行程序的命令与完整参数. -c:取消同名程序,合并显示. - ...

  6. android rsa 示例

    1.参考资料 1.1 android的Cipher官方文档 https://developer.android.com/reference/javax/crypto/Cipher 其中 构造Ciphe ...

  7. java中的线程(2):如何正确停止线程之2种常见停止方式

    1.常见停止方式 结束run函数,run中含退出标志位. 使用interrupt()方法中断线程 使用stop方法暴力终止(已经弃用) 2.结束run class TestThread extends ...

  8. Django项目:CRM(客户关系管理系统)--05--02PerfectCRM创建ADMIN页面03

    /*! *bootstrap.js * * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. ...

  9. Django用户名密码错误提示

    from django.shortcuts import render # Create your views here. from django.shortcuts import render fr ...

  10. 灵动微本土MCU厂商具有吸引力的增长点

    作为各种电子产品的控制和处理核心,微控制单元(MCU)器件是一种集成微处理器(CPU).存储器(RAM/ROM).计数器,以及I/O端口的芯片.从MCU内核架构来看,单片机有历经多年的8051,基于A ...