题目描述

给定长度为n的序列:a1,a2,...,an,记为a[1:n]。类似地,a[l:r](1<=l<=r<=N)是指序列:al,al+1,...,ar-1,ar。若1<=l<=s<=t<=r<=n,则称a[s:t]是a[l:r]的子序列。现在有q个询问,每个询问给定两个数l和r,1<=l<=r<=n,求a[l:r]的子序列的最小值之和。例如,给定序列5,2,4,1,3,询问给定的两个数为1和3,那么a[1:3]有6个子序列a[1:1],a[2:2],a[3:3],a[1:2],a[2:3],a[1:3],这6个子序列的最小值之和为5+2+4+2+2+2=17。

分析

关于ST表的分析

对于静态区间的最值查找,一般都是由ST来实现的,优点有以下三点:

  • 代码量少
  • 时间复杂度较小,一般的复杂度是\(O(nk)\)的复杂度。
  • 空间占有量少,空间复杂度一般是\(O(nk)\)的复杂度。
    但是ST表无法处理待修的问题,也就是动态区间最值问题。但是对于这道题,st表是非常好的选择。

关于莫队的分析

那么在此反观这道题目,我们可以发现一个性质:如果我们当前算出了区间\([l,r]\)的答案,如果要更新到区间\([l,r+1]\)或者是\([l-1,r]\)的话,我们需要查找的答案就是在这个新的加进来的数中。
但是我们原来的答案中的子序列都有增加,简单点来说就是我们有新增的子序列\([l,r+1],[l+1,r+1]..[r+1,r+1]\),这些区间的最小值的处理是我们需要思考的。
这样的问题我们可以用莫队来解决。

关于前缀和

因为区间最小值,然后扩大了范围,那么我们可以定义数组\(lst\)表示从左边开始第一个比\(a_i\)小的数字,而\(nxt\)表示右边。
这个计算就像用单调栈维护的最长上升子序列的思路一样,用单调栈计算出这个值,复杂度是\(O(n)\)。
那么对于这个值,那么我们就可以结局最小值的问题,但是题目中给出的是贡献的计算,那么我们就用前缀和计算出\([1,l]\)和\([r,n]\)对于答案的贡献,还是需要正的做一遍,倒着做一遍。

ac代码

#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define LL long long
#define N 1000005
using namespace std;
inline char gc() {
    static char buf[1 << 16], *S, *T;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 1 << 16, stdin);
        if (T == S) return EOF;
    }
    return *S ++;
}
template <typename T>
inline void read(T &x) {
    T w = 1;
    x = 0;
    char ch = gc();
    while (ch < '0' || ch > '9') {
        if (ch == '-') w = -1;
        ch = gc();
    }
    while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc();
    x = x * w;
}
template <typename T>
void write(T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + 48);
}
struct Stack {
    int s[N], top;
    Stack() {
        memset(s, 0, sizeof(s));
        top = 0;
    }
    void push(int x) {
        s[++ top] = x;
    }
    void pop() {
        --top;
    }
    int get_top() {
        return s[top];
    }
}s;
struct rec_que {
    int l, r, pos, id;
    bool operator <(const rec_que &rhs) const {
        return pos == rhs.pos? r < rhs.r: pos < rhs.pos;
    }
}q[N];
int Log[N], a[N], f[N][22], lst[N], nxt[N], po[N];
LL res, ans[N], suml[N], sumr[N];
int l, r, n, m, block;
void update(int &x, int y, int z) {
    a[y] < a[z] ? x = y : x = z;
}
int query(int l, int r) {
    if (l > r) swap(l, r);
    int k = Log[r - l + 1];
    return a[f[l][k]] < a[f[r - po[k] + 1][k]]? f[l][k]: f[r - po[k] + 1][k];
}
void update1(int l, int r, int fg) {
    int k = query(l, r);
    res += fg *(1ll* a[k] * (r - k + 1) + sumr[l] - sumr[k]);
}
void update2(int l, int r, int fg) {
    int k = query(l, r);
    res += fg *(1ll *a[k] * (k - l + 1) + suml[r] - suml[k]);
}
int main() {
//  freopen("line.in","r",stdin);
//  freopen("line.out","w",stdout);
    read(n); read(m);
    block = sqrt(n);
    Log[0] = -1;
    for (int i = 1; i <= n; i ++) {
        read(a[i]);
        f[i][0] = i;
        Log[i] = Log[i / 2] + 1;
    }
    for (int i = 1; i <= m; i ++) {
        read(q[i].l); read(q[i].r);
        q[i].pos = (q[i].l - 1) / block + 1;
        q[i].id = i;
    }
    po[0] = 1;
    for (int i = 1; i <= 16; i ++) po[i] = po[i - 1]<< 1;
    for (int j = 1; j <= 16; j ++)
        for (int i = 1; i <= n; i ++) {
            f[i][j] = f[i][j - 1];
            if (i + po[j - 1] <= n) update(f[i][j], f[i][j - 1], f[i + po[j - 1]][j - 1]);
        }
    for (int i = 1; i <= n; i ++) {
        while (s.top&& a[s.get_top()] >= a[i]) {
            nxt[s.get_top()] = i;
            s.pop();
        }
        s.push(i);
    }
    while (s.top) {
        nxt[s.get_top()] = n + 1;
        s.pop();
    }
    for (int i = n; i >= 1; i --) {
        while (s.top&&a[s.get_top()] > a[i]) {
            lst[s.get_top()] = i;
            s.pop();
        }
        s.push(i);
    }
    while (s.top) {
        lst[s.get_top()] = 0;
        s.pop();
    }
    for (int i = 1; i <= n; i ++) suml[i] = suml[lst[i]] + (LL)(i - lst[i]) * a[i];
    for (int i = n; i >= 1; i --) sumr[i] = sumr[nxt[i]] + (LL)(nxt[i] - i) * a[i];
    sort(q + 1, q + 1 + m);
    res = a[1];
    l = 1, r = 1;
    for (int i = 1; i <= m; i ++) {
        while (r < q[i].r) r ++, update2(l, r, 1);
        while (l > q[i].l) l --, update1(l, r, 1);
        while (r > q[i].r) update2(l, r, -1), r --;
        while (l < q[i].l) update1(l, r, -1), l ++;
        ans[q[i].id] = res;
    }
    for (int i = 1; i <= m; i ++) {
        write(ans[i]);
        puts("");
    }
    return 0;
}

[luogu3246][bzoj4540][HNOI2016]序列【莫队+单调栈】的更多相关文章

  1. [bzoj4540][Hnoi2016][序列] (莫队算法+单调栈+st表)

    Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-1,ar.若1≤l≤s≤t≤r≤n,则称a ...

  2. 【HNOI2016】序列 莫队+单调栈+RMQ

    Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,ar-1,ar.若1≤l≤s≤t≤r≤n,则称a ...

  3. [BZOJ4540][HNOI2016]序列 莫队

    4540: [Hnoi2016]序列 Time Limit: 20 Sec  Memory Limit: 512 MB Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n ...

  4. [HNOI2016]序列(莫队,RMQ)

    [HNOI2016]序列(莫队,RMQ) 洛谷  bzoj 一眼看不出来怎么用数据结构维护 然后还没修改 所以考虑莫队 以$(l,r-1) -> (l,r)$为例 对答案的贡献是$\Sigma_ ...

  5. 【BZOJ4540】[Hnoi2016]序列 莫队算法+单调栈

    [BZOJ4540][Hnoi2016]序列 Description 给定长度为n的序列:a1,a2,…,an,记为a[1:n].类似地,a[l:r](1≤l≤r≤N)是指序列:al,al+1,…,a ...

  6. BZOJ.4540.[HNOI2016]序列(莫队/前缀和/线段树 单调栈 RMQ)

    BZOJ 洛谷 ST表的一二维顺序一定要改过来. 改了就rank1了哈哈哈哈.自带小常数没办法. \(Description\) 给定长为\(n\)的序列\(A_i\).\(q\)次询问,每次给定\( ...

  7. BZOj 4540: [Hnoi2016]序列 [莫队 st表 预处理]

    4540: [Hnoi2016]序列 题意:询问区间所有子串的最小值的和 不强制在线当然上莫队啦 但是没想出来,因为不知道该维护当前区间的什么信息,维护前后缀最小值的话不好做 想到单调栈求一下,但是对 ...

  8. BZOJ.4826.[AHOI/HNOI2017]影魔(树状数组/莫队 单调栈)

    BZOJ LOJ 洛谷 之前看\(mjt\)用莫队写了,以为是一种正解,码了3h结果在LOJ T了没A= = 心态爆炸(upd:发现是用C++11(NOI)交的,用C++11交就快一倍了...) 深刻 ...

  9. 洛谷P3246 [HNOI2016]序列 [莫队]

    传送门 思路 看到可离线.无修改.区间询问,相信一定可以想到莫队. 然而,莫队怎么转移是个大问题. 考虑\([l,r]\rightarrow[l,r+1]\)时答案会怎样变化?(左端点变化时同理) \ ...

随机推荐

  1. oracle导出用户下单表或者多表,导入到别的服务器用户下

      导出   exp 用户名/密码 file=存放dmp的名称的目录 statistics=none tables =(表名,表名,表名) exp creditfw/credit file=d:\te ...

  2. 2 Modals of necessity

    1 情况动词must 和词组have to, need to ,have got to 都表示必须做某事或者要求做某事. need to  have to  must have got to When ...

  3. Chrome---谷歌浏览器修改用户缓存文件夹 如何设置缓存路径

    1.首先我们在电脑上打开chrome浏览器,然后地址栏输入chrome://Version,然后按下回车键,找到个人资料路径一项. 2.接下来我们选中个人资料路径后面所有的信息,右键点击信息后选择“复 ...

  4. web service 异常

    1.org/apache/commons/discovery/tools/DiscoverSingleton Exception in thread "main" java.lan ...

  5. Pyspark spark-submit 集群提交任务以及引入虚拟环境依赖包攻略

    网上提交 scala spark 任务的攻略非常多,官方文档其实也非常详细仔细的介绍了 spark-submit 的用法.但是对于 python 的提交提及得非常少,能查阅到的资料非常少导致是有非常多 ...

  6. Spring boot 全局配置文件application.properties

    #更改Tomcat端口号 server.port=8090 #修改进入DispatcherServlet的规则为:*.htmlserver.servlet-path=*.html#这里要注意高版本的s ...

  7. /proc/diskstats

    读取磁盘统计信息,如下所示: linux-HpdBKE:~ # cat /proc/diskstats sda sda1 sda2 dm- dm- dm- sda为整个硬盘的统计信息,sda1为第一个 ...

  8. 学习 Spring (六) 自动装配

    Spring入门篇 学习笔记 No: (默认)不做任何操作 byName: 根据属性名自动装配.此选项将检查容器并根据名字查找与属性完全一致的 bean,并将其与属性自动装配 byType: 如果容器 ...

  9. A Simple Problem with Integers(线段树区间更新模板)

    最基本的线段树的区间更新及查询和 用tag(lazy)数组来“延缓”更新,查询或添加操作必须进行pushdown操作,即把tag从p传到lp和rp并清楚tag[p],既然得往lp和rp递归,那么就可以 ...

  10. codeforces559B

    Equivalent Strings CodeForces - 559B Today on a lecture about strings Gerald learned a new definitio ...