@atcoder - AGC037F@ Counting of Subarrays
@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@
#include<cstdio>
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
#define mp make_pair
#define fi first
#define se second
const int MAXN = 200000;
typedef pair<int, int> pii;
typedef long long ll;
priority_queue<pii, vector<pii>, greater<pii> >que;
int lst[MAXN + 5], nxt[MAXN + 5], N, L;
void link(int x, int y) {lst[y] = x, nxt[x] = y;}
bool check(int x, int y) {return nxt[x] == y;}
ll lf[MAXN + 5], rf[MAXN + 5], f1[MAXN + 5], f2[MAXN + 5];
vector<int>v1, v2;
ll solve(int x) {
ll ret = 0, tmp = 0;
int lt = v2.size(), lb = lst[v2[0]], rb = nxt[v2[lt-1]];
for(int i=0;i<lt;i++) {
if( i - L + 1 >= 0 ) tmp += lf[v2[i-L+1]];
ret += tmp * rf[v2[i]];
}
for(int i=0;i<lt;i++)
f1[v2[i]] = lf[v2[i]], f2[v2[i]] = rf[v2[i]], lf[v2[i]] = rf[v2[i]] = 0;
int c = lt / L;
if( c ) {
for(int i=L-1;i<lt;i++) {
int t = (i + 1)/L - 1;
rf[v2[t]] += f2[v2[i]];
}
for(int i=lt-L;i>=0;i--) {
int t = c - (lt - i)/L;
lf[v2[t]] += f1[v2[i]];
}
for(int i=1;i<c;i++)
link(v2[i-1], v2[i]);
link(lb, v2[0]), link(v2[c-1], rb);
for(int i=0;i<c;i++)
que.push(mp(x + 1, v2[i]));
tmp = 0;
for(int i=0;i<c;i++) {
if( i - L + 1 >= 0 ) tmp += lf[v2[i-L+1]];
ret -= tmp * rf[v2[i]];
}
}
else nxt[lb] = N + 1, lst[rb] = 0;
v2.clear();
return ret;
}
int main() {
scanf("%d%d", &N, &L);
for(int i=1;i<=N;i++) {
int x; scanf("%d", &x);
que.push(mp(x, i)), link(i, i + 1);
lf[i] = rf[i] = 1;
}
ll ans = 0; link(0, 1);
while( !que.empty() ) {
int x = que.top().fi; v1.clear();
while( !que.empty() && que.top().fi == x )
v1.push_back(que.top().se), que.pop();
v2.clear(); v2.push_back(v1[0]);
for(int i=1;i<v1.size();i++) {
if( !check(v1[i-1], v1[i]) )
ans += solve(x);
v2.push_back(v1[i]);
}
ans += solve(x);
}
printf("%lld\n", ans + N);
}
@details@
为什么会有一种AGC的F题变水了的错觉
即使想到了这个方向,也很难说能够把所有细节想清楚吧。
嗯。应该是这样的。
@atcoder - AGC037F@ Counting of Subarrays的更多相关文章
- AtCoder Grand Contest 037
Preface 这篇咕了可能快一个月了吧,正好今天晚上不想做题就来补博客 现在还不去复习初赛我感觉我还是挺刚的(微笑) A - Dividing a String 考虑最好情况把每个字符串当作一个来看 ...
- AtCoder Grand Contest 037 简要题解
从这里开始 题目目录 Problem A Dividing a String 猜想每段长度不超过2.然后dp即可. 考虑最后一个长度大于等于3的一段,如果划成$1 + 2$会和后面相同,那么划成$2 ...
- @atcoder - CODE FESTIVAL 2017 Elimination Tournament Round 3 F@ Unicyclic Graph Counting
目录 @description@ @solution@ @accpeted code@ @details@ @description@ 求有多少 n 点 n 边的无向连通图,满足第 i 个点的度数为 ...
- AtCoder Grand Contest 005
AtCoder Grand Contest 005 A - STring 翻译 给定一个只包含\(ST\)的字符串,如果出现了连续的\(ST\),就把他删去,然后所有位置前移.问最后剩下的串长. 题解 ...
- AtCoder Grand Contest 023 A - Zero-Sum Ranges
Time limit : 2sec / Memory limit : 256MB Score : 200 points Problem Statement We have an integer seq ...
- Atcoder/Topcoder 口胡记录
Atcoder/Topcoder 理论 AC Atcoder的❌游戏示范 兴致勃勃地打开一场 AGC 看 A 题,先 WA 一发,然后花了一年时间 Fix. 看 B 题,啥玩意?这能求? 睡觉觉. e ...
- 萌新笔记——Cardinality Estimation算法学习(二)(Linear Counting算法、最大似然估计(MLE))
在上篇,我了解了基数的基本概念,现在进入Linear Counting算法的学习. 理解颇浅,还请大神指点! http://blog.codinglabs.org/articles/algorithm ...
- POJ_2386 Lake Counting (dfs 错了一个负号找了一上午)
来之不易的2017第一发ac http://poj.org/problem?id=2386 Lake Counting Time Limit: 1000MS Memory Limit: 65536 ...
- ZOJ3944 People Counting ZOJ3939 The Lucky Week (模拟)
ZOJ3944 People Counting ZOJ3939 The Lucky Week 1.PeopleConting 题意:照片上有很多个人,用矩阵里的字符表示.一个人如下: .O. /|\ ...
随机推荐
- Windows 禁用Windows updata服务
方法一:禁用Windows updata服务 按WIN+R 打开运行,输入 services.msc 回车 然后找到 “Windows updata”服务,双击后设置为禁用 应用即可; 方法二:推迟自 ...
- Java review-basic5
1. How would you write a socket client/server in Java The server DateServer.java package edu.lmu.cs. ...
- 下载android4.4.2源代码全过程(附已下载的源代码)
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/maosidiaoxian/article/details/36625727 今天在下载andriod ...
- Ajax--PHP+JQuery+Ajax解析json、XML数据、加载页面
一.JQuery+Ajax用get.post方式提交和请求数据 知识要点: $('#userName').blur(function () { var txt = $(this).val(); $.a ...
- 使用openpyxl模块时出现错误: zipfile.BadZipFile: File is not a zip file
通过Pycharm工具新建一个xlsx文件. 再通过openpyxl模块读取该表时,报错: zipfile.BadZipFile: File is not a zip file 如下所示: 解决办法: ...
- Linux操作系统各版本ISO镜像下载(包括oracle linux\redhat\centos\u
Linux操作系统各版本ISO镜像下载(包括oracle linux\redhat\centos\ubuntu\debian等) 1.Oracle Linux(下载地址) (1)OracleLinux ...
- BZOJ 4420二重镇题解
链接 思路借鉴了这个博客: 我们可以想到状压dp 用一个十进制数来表示状态,即第i位表示位置i处的物品等级 用f[i][j][k]表示第i天,仓库的物品等级为j,状态为k时的最大收益 但是状态数貌似很 ...
- BZOJ 1925地精部落题解
题目链接 一道神仙题,有很多思考的方式,这里选择最好理解的一种来讲 我们将序列分为两种,一种开头递增,一种开头递减,显然这两种序列的数目是一样的 现在我们只用考虑开头递增的情况 f[i][j]表示前i ...
- tar解压.tar.bz2文件失败:tar: Error is not recoverable: exiting now
使用tar解压.tar.bz2文件: tar -jxvf xxxx.tar.bz2 报如下错误: 原因:未安装bzip yum -y install bzip2
- ChaosBlade 发布对 C++ 应用混沌实验的支持
前言 为满足 C++ 应用系统故障演练,阿里妈妈安全生产团队开源了 C++ 混沌实验执行器,填补了 C++ 应用混沌工程实验的空白,其遵循<混沌实验模型>,可通过 ChaosBlade 工 ...