Problem

Description

给定长度为 \(n\) 的序列:\(a_1, a_2, \cdots , a_n\),记为 \(a[1 \colon n]\)。类似地,\(a[l \colon r]\)(\(1 \leq l \leq r \leq N\))是指序列:\(a_{l}, a_{l+1}, \cdots ,a_{r-1}, a_r\)。若 \(1\leq l \leq s \leq t \leq r \leq n\),则称 \(a[s \colon t]\)是 \(a[l \colon r]\) 的子序列。

现在有 \(q\) 个询问,每个询问给定两个数 \(l\) 和 \(r\),\(1 \leq l \leq r \leq n\),求 \(a[l \colon r]\) 的不同子序列的最小值之和。例如,给定序列

\(5, 2, 4, 1, 3\),询问给定的两个数为 \(1\) 和 \(3\),那么 \(a[1 \colon 3]\) 有 \(6\) 个子序列 \(a[1 \colon 1], a[2 \colon 2], a[3 \colon 3], a[1 \colon 2],a[2 \colon 3], a[1 \colon 3]\),这 \(6\) 个子序列的最小值之和为 \(5+2+4+2+2+2=17\)。

Input Format

输入文件的第一行包含两个整数 \(n\) 和 \(q\),分别代表序列长度和询问数。

接下来一行,包含 \(n\) 个整数,以空格隔开,第 \(i\) 个整数为 \(a_i\),即序列第 \(i\) 个元素的值。

接下来 \(q\) 行,每行包含两个整数 \(l\) 和 \(r\),代表一次询问。

Output Format

对于每次询问,输出一行,代表询问的答案。

Sample

Input

5 5
5 2 4 1 3
1 5
1 3
2 4
3 5
2 5

Output

28
17
11
11
17

Range

对于 \(100\%\) 的数据,\(1 \leq n,q \leq 100000 ,|a_i| \leq 10^9\)

Algorithm

莫队

Mentality

第一眼觉得做法和 \(HNOI2017\) 的影魔应该是一样的,然后发现由于这一题的区间最小值可能存在多个,那么影魔里的就完全不适用了 \(QwQ\) 。

那看着这个数据范围,我们能想到三种复杂度:\(nlog\) 、\(nlog^2\)、\(n\sqrt{n}\) 。

然后发现可以离线,询问是区间形式的,我们便不由得想到莫队了。

接下来考虑莫队里的计算步骤:从 \([l,r]\) -> \([l,r+1]\) 的增量,由于其他三个计算本质相同,不多做讨论。

首先,我们设 \(p\) 为区间 \([l,r+1]\) 的最小值所在位置,那么对于区间左端点在 \(l\sim p\) ,右端点在 \(r+1\) 的区间,它们的最小值肯定都是 \(a[p]\) 。则这一坨区间对答案的贡献为 \(a[p]*(p-l+1)\) 。

那剩下的左端点在 \(p+1\sim r\) 的区间的贡献呢?

其实我们可以稍加思考,就会发现我们应该考虑先处理出两个 \(ll[i],rr[i]\) 数组,分别是 \(i\) 左边第一个比 \(i\) 小的位置,\(i\) 右边第一个比 \(i\) 小的位置。

那么我们设 \(f_l[r+1]\) 右端点在 \(r+1\) ,而左端点在 \([1,r+1]\) 范围的区间的最小值之和:

\[f_l[r+1]=(r+1-ll[r+1])*a[r]+(ll[r+1]-ll[ll[r+1]])*a[ll[r+1]]+\dots + 0
\]

也即按照区间最小值分段,只要右端点固定,那么区间的最小值肯定是连续相同的,我们一个个区间去计算就好了。

然后我们观察到反正这个式子是从 \(ll[r+1]\) 把值转移上来的,那我们自然可以写出如下 \(DP\) 式:

\[f_l[r+1]=(r+1-ll[r+1])*a[r]+f_l[ll[r+1]]
\]

不难发现,对于原式子中,我们沿着一段段连续的区间最小值计算答案,而对于其中任意一个 "\(ll[i]\)" ,我们只需要把后面那截砍掉,也即 \(f_l[r+1]-f_l[ll[i]]\) ,这显然就是右端点在 \(r+1\) ,左端点在 \([ll[i]+1,r+1]\) 这段范围内的区间最小值之和。

由于 \(p\) 已经是区间内最小的位置了,所以对于 \(p+1\sim r\) 这些点,它们的 \(ll[i]\) 的值要么就是 \(a[p]\) ,要么就不小于 \(a[p]\)。所以 \(p\) 一定是计算 \(r+1\) 的答案中的某个 "\(ll[i]\)" ,那我们只需要用 \(f_l[r+1]\) 减去 \(f_l[p]\) 即可得到左端点在 \(p+1\sim r\) 的区间的答案。

总结一下,\([l,r]\) -> \([l,r+1]\) 的增量为:

\[a[p]*(p-l+1)+f_l[r+1]-f_l[p]
\]

对于 \([l,r]\) -> \([l-1,r]\) 也同理,我们只需要处理出一个类似的 \(f_r\) 数组即可。

Code

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
int n, size, Q, a[100001];
int minn[100001][18], pos[100001][18], Log[100001];
int top, ll[100001], rr[100001], stack[100001];
int L, R;
long long ans, Ans[100001], fr[100001], fl[100001];
struct node {
int l, r, d;
} k[100001];
bool cmp(node a, node b) {
return (a.l / size) == (b.l / size) ? a.r < b.r : (a.l / size) < (b.l / size);
}
int find(int l, int r) {
if (l > r) return 0;
if (l == r) return pos[l][0];
int x = Log[r - l], p;
return minn[l][x] > minn[r - (1 << x) + 1][x] ? pos[r - (1 << x) + 1][x]
: pos[l][x]; } //寻找最小值位置
void init() {
cin >> n >> Q;
size = sqrt(n);
int now = 2;
for (int i = 2; i <= (int)1e5; i++) {
Log[i] = Log[i - 1];
if (i == now) Log[i]++, now <<= 1;
} //预处理对数
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
minn[i][0] = a[i], pos[i][0] = i;
}
for (int i = 1; i <= Q; i++) scanf("%d%d", &k[i].l, &k[i].r), k[i].d = i;
sort(k + 1, k + Q + 1, cmp); //离线询问
for (int j = 1; j <= 17; j++)
for (int i = 1; i <= n - (1 << j) + 1; i++) {
pos[i][j] = pos[i][j - 1],
minn[i][j] = min(minn[i][j - 1], minn[i + (1 << (j - 1))][j - 1]);
if (minn[i][j] != minn[i][j - 1])
pos[i][j] = pos[i + (1 << (j - 1))][j - 1];
} //预处理 rmq
stack[top = 0] = 0;
for (int i = 1; i <= n; i++) {
while (top && a[stack[top]] >= a[i]) top--;
ll[i] = stack[top], stack[++top] = i;
}
for (int i = 1; i <= n; i++) fl[i] = fl[ll[i]] + 1ll * (i - ll[i]) * a[i];
stack[top = 0] = n + 1;
for (int i = n; i >= 1; i--) {
while (top && a[stack[top]] >= a[i]) top--;
rr[i] = stack[top], stack[++top] = i;
} //单调栈处理 ll,rr 数组
for (int i = n; i >= 1; i--)
fr[i] = fr[rr[i]] + 1ll * (rr[i] - i) * a[i]; //计算 fl,fr 数组
}
long long workr(int x) {
int p = find(L, x);
return 1ll * a[p] * (p - L + 1) + fl[x] - fl[p];
}
long long workl(int x) {
int p = find(x, R);
return 1ll * a[p] * (R - p + 1) + fr[x] - fr[p];
}
void solve() {
L = k[1].l, R = k[1].l - 1;
for (int i = 1; i <= Q; i++) {
while (R < k[i].r) ans += workr(++R);
while (L > k[i].l) ans += workl(--L);
while (R > k[i].r) ans -= workr(R--);
while (L < k[i].l) ans -= workl(L++);
Ans[k[i].d] = ans;
}
}
int main() {
init(); //预处理和读入
solve();
for (int i = 1; i <= Q; i++) printf("%lld\n", Ans[i]);
}

【HNOI 2016】序列的更多相关文章

  1. [HNOI 2016]序列

    Description 题库链接 给你一个长度为 \(n\) 的序列 \(A\) ,给出 \(q\) 组询问.每次询问 \([l,r]\) ,求该区间内所有的子序列中最小值的和. \(1\leq n, ...

  2. bzoj 4540 [HNOI 2016] 序列 - 莫队算法 - Sparse-Table - 单调栈

    题目传送门 传送点I 传送点II 题目大意 给定一个长度为$n$的序列.询问区间$[l, r]$的所有不同的子序列的最小值的和. 这里的子序列是连续的.两个子序列不同当且仅当它们的左端点或右端点不同. ...

  3. [HNOI 2016]树

    Description 题库链接 给你一棵 \(N\) 个节点根节点为 \(1\) 的有根树,结点的编号为 \(1\sim N\) :我们称这颗树为模板树.需要通过这棵模板树来构建一颗大树.构建过程如 ...

  4. 「HNOI 2016」 序列

    \(Description\) 给你一个序列,每次询问一个区间,求其所有子区间的最小值之和 \(Solution\) 这里要用莫队算法 首先令\(val\)数组为原序列 我们考虑怎么由一个区间\([l ...

  5. hnoi 2016 省选总结

    感觉省选好难的说...反正我数据结构太垃圾正解想到了也打不出来打一打暴力就滚粗了! DAY1 0+20+30 DAY2 60-20+0+60 最后170-20分,暴力分还是没有拿全! 然而这次是给了5 ...

  6. HNOI 2016 省队集训日记

    第一天 DeepDarkFantasy 从东京出发,不久便到一处驿站,写道:日暮里.  ——鲁迅<藤野先生> 定义一个置换的平方为对1~n的序列做两次该置换得到的序列.已知一个置换的平方, ...

  7. 【HNOI 2016】网络

    Problem Description 一个简单的网络系统可以被描述成一棵无根树.每个节点为一个服务器.连接服务器与服务器的数据线则看做一条树边.两个服务器进行数据的交互时,数据会经过连接这两个服务器 ...

  8. [BZOJ 4537][Hnoi 2016]最小公倍数

    传送门 并查集+分块 看到题目可以想到暴力做法, 对于每个询问, 将所有a和b小于等于询问值的的边加入图中(用并查集), 如果询问的u和v在一个联通块中, 且该联通块的maxa和maxb均等与询问的a ...

  9. 【BZOJ 4539】【HNOI 2016】树

    http://www.lydsy.com/JudgeOnline/problem.php?id=4539 今天测试唯一会做的一道题. 按题目要求,如果暴力的把模板树往大树上仍,最后得到的大树是$O(n ...

随机推荐

  1. 精心调制的Bash主题分享

    第一个是米色背景, 一样遵守蓝色为目录的习惯, 字体为浅灰色,代码和终端看起来相对比较清晰 第二个则是 深海蓝的背景风格,字体都选择饱和度较高的颜色,如下图 终端 配置方法,这里以 mintty 也就 ...

  2. SQL使用之关联更新、批量插入

    使用场景 某个字段数据异常,利用另外一张表同步修改该表异常字段的数据; 关联更新 UPDATE tableName1 AS t1 LEFT JOIN tableName12 AS t2 ON t1.x ...

  3. minikube windows hyperx填坑记

    minikube windows hyperx填坑记 安装了一天半,还是没行,先放弃 开始 minikube start --vm-driver=hyperv --hyperv-virtual-swi ...

  4. 怎么eclipse或MyEclipse中添加javaSe的源码

    怎么eclipse或MyEclipse中添加javaSe的源码 有时在eclipse里我们调用java提供给我们的方法,我们有时需要查看java提供给我们的调用方法的源码或java提供给我们的核心类的 ...

  5. 表情的战争(App名称)技术服务支持

    1.进入游戏走过场动画,可以点击退出跳过此过场动画: 2.进入主界面后直接点击开始游戏进入场景跑图,进入npc对话面板,对话结束进入战斗面板: 3.战斗操作方法为玩家拖动表情牌,进行攻击或者防守,直至 ...

  6. U盘安装Windows原版系统(安装方式有很多,我讲我的安装方式)

    我陈某人,也是安装过至少200部台式或笔记本的人物. 低调,低调,开个玩笑~ 安装方式有很多,我讲我的安装方式,欢迎收藏. 1.下载准备文件下载.iso原版系统镜像文件http://msdn.itel ...

  7. requests 获取百度推广信息

    2019年的第一篇博客,恩,好久没写过博客了,恩,忘了,哈哈,实在是太懒了 今天写一个爬取百度推广数据的爬虫,当然我写的肯定不是那么的完美,但是能用,大哭 注意:有的时候,get或post方法获取数据 ...

  8. 关于Linux目录结构的理解

    dUI与刚接触Linux的学习者来说,那么多的根下目录足够让我们头疼不已,如下图: 那么对于初学者来说,我们首要了解的是哪些目录呢?  就是这个标黄绿色的tmp目录,此目录是一个存放临时文件夹的目录( ...

  9. 求你显示pdf

    123 <iframe src='http://km.shengaitcm.com/ADC/_layouts/15/WopiFrame.aspx?sourcedoc=%2FADC%2FDocLi ...

  10. RNA-seq标准化

    你的 heatmap 可能用错数据了 (组间表达量标准化) http://www.genek.tv/article/24 RNA-seq的标准化方法罗列 https://www.jianshu.com ...