[HNOI 2016]序列

Description

题库链接

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

\(1\leq n,q\leq 100000,|A_i|\leq 10^9\)

Solution

考虑把右端点右移时,会产生 \(r-l+1\) 个新的区间,我们可以来统计这 \(r-l+1\) 个区间的最小值和。

记 \(pre_i\) 为从第 \(i\) 位往左走第一个值比 \(A_i\) 小的位置。

显然在 \(l'\in(pre_r,r]\) 的区间 \([l',r]\) 中的最小值为 \(A_r\) 。

我们可以用这个性质做一遍前缀。记 \(sum_i\) 为右端点为 \(i\) 时,左端点在 \([1,i]\) 这段区间内所有区间中最小值的和。很容易用单调栈预处理出来。

那么对于移动右端点时,我们记 \(loc\) 为这段区间内的最小值所在的位置,显然移动右端点产生的贡献为 \(sum_r-sum_{loc}+A_{loc}\cdot(loc-l+1)\) 。

查询最小值可以用 \(st\) 表来解决。

对于移动左端点也类似,从右往左处理就好了。其余的就用莫队来实现。

Code

//It is made by Awson on 2018.2.12
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('\n'))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 100000;
void read(int &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}
void print(LL x) {if (x > 9) print(x/10); putchar(x%10+48); }
void write(LL x) {if (x < 0) putchar('-'); print(Abs(x)); }

#define log2 LOG
int n, q, lim, block, a[N+5], log2[N+5], pre[N+5], nex[N+5], f[N+5][20], bin[25];
LL suml[N+5], sumr[N+5], ans[N+5];
int s[N+5], top;
struct tt {
    int l, r, id;
    bool operator < (const tt &b) const {return l/block == b.l/block ? r < b.r : l < b.l; }
}qry[N+5];

int query(int l, int r) {int lim = log2[r-l+1]; return a[f[l][lim]] < a[f[r-bin[lim]+1][lim]] ? f[l][lim] : f[r-bin[lim]+1][lim]; }
LL movel(int l, int r) {int loc = query(l, r); return sumr[l]-sumr[loc]+1ll*a[loc]*(r-loc+1); }
LL mover(int l, int r) {int loc = query(l, r); return suml[r]-suml[loc]+1ll*a[loc]*(loc-l+1); }
void work() {
    read(n), read(q); for (int i = 1; i <= n; i++) read(a[i]), f[i][0] = i;
    log2[0] = -1; for (int i = 1; i <= n; i++) log2[i] = log2[i>>1]+1;
    bin[0] = 1; for (int i = 1; i <= 20; i++) bin[i] = bin[i-1]<<1;
    lim = log2[n], block = sqrt(n);
    for (int i = 1; i <= n; i++) {
        while (top != 0 && a[s[top]] >= a[i]) --top;
        pre[i] = top == 0 ? 0 : s[top]; s[++top] = i;
    }
    s[top = 0] = 0;
    for (int i = n; i >= 1; i--) {
        while (top != 0 && a[s[top]] >= a[i]) --top;
        nex[i] = top == 0 ? n+1 : s[top]; s[++top] = i;
    }
    for (int i = 1; i <= n; i++) suml[i] = suml[pre[i]]+1ll*(i-pre[i])*a[i];
    for (int i = n; i >= 1; i--) sumr[i] = sumr[nex[i]]+1ll*(nex[i]-i)*a[i];
    for (int i = 1; i <= lim; i++) for (int j = 1; j <= n; j++) {
        if (j+bin[i-1] > n) break;
        f[j][i] = a[f[j][i-1]] < a[f[j+bin[i-1]][i-1]] ? f[j][i-1] : f[j+bin[i-1]][i-1];
    }
    for (int i = 1; i <= q; i++) read(qry[i].l), read(qry[i].r), qry[i].id = i;
    sort(qry+1, qry+1+q);
    int curl = 1, curr = 0; LL now = 0;
    for (int i = 1; i <= q; i++) {
        int l = qry[i].l, r = qry[i].r;
        while (curr < r) ++curr, now += mover(curl, curr);
        while (curl > l) --curl, now += movel(curl, curr);
        while (curr > r) now -= mover(curl, curr), --curr;
        while (curl < l) now -= movel(curl, curr), ++curl;
        ans[qry[i].id] = now;
    }
    for (int i = 1; i <= q; i++) writeln(ans[i]);
}
int main() {
    work(); return 0;
}

原文地址:https://www.cnblogs.com/NaVi-Awson/p/8457069.html

时间: 2024-10-10 07:24:54

[HNOI 2016]序列的相关文章

HNOI 2016 省队集训日记

第一天 DeepDarkFantasy 从东京出发,不久便到一处驿站,写道:日暮里.  ——鲁迅<藤野先生> 定义一个置换的平方为对1~n的序列做两次该置换得到的序列.已知一个置换的平方,并且这个结果是一个排列,求该置换. 输入第一行一个数n表示排列长度,接下来一行n个数描述排列. 有解则输出一行n个数表示原排列.否则输出一行一个-1. 测试点编号 特征 0~1 n<=10 2~9 n<=1000000 此题有spj. 考试的时候懵逼了,根本没想清就开始乱打. 题解:由题易得每一个

HNOI 2016 乱做

  4542: [Hnoi2016]大数 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 488  Solved: 182[Submit][Status][Discuss] Description 小 B 有一个很大的数 S,长度达到了 N 位:这个数可以看成是一个串,它可能有前导 0,例如00009312345.小B还有一个素数P.现在,小 B 提出了 M 个询问,每个询问求 S 的一个子串中有多少子串是 P 的倍数(0 也是P 的倍数).例如

HNOI 2016 地图

[题目描述] Hoshizora Rin是个特别好动的少女. 一天Rin来到了一个遥远的都市.这个都市有N个建筑,编号从1到N,其中市中心编号为1,这个都市有M条双向通行的街道,每条街道连接着两个建筑,其中某些街道首尾相连连接成了一个环.Rin通过长时间的走访,已经清楚了这个都市的两个特点: 从市中心出发可以到达所有的建筑物. 任意一条街道最多存在与一个简单环中. 令Rin心花怒放的是,每个建筑物都会有拉面售卖.拉面有很多不同的种类,但对于Rin而言只有油腻程度的不同,因此我们把油腻程度相同的拉

【BZOJ 4539】【HNOI 2016】树

http://www.lydsy.com/JudgeOnline/problem.php?id=4539 今天测试唯一会做的一道题. 按题目要求,如果暴力的把模板树往大树上仍,最后得到的大树是$O(n^2)$级别的,不能存储,更不能做了. 把模板树往大树上扔的过程我想象成了两个大节点进行连边,每个大节点代表模板树本身或一部分. 这相当于把初始的大树(此时和模板树相同)缩成一个大节点,每次把模板树的一部分缩成一个大节点往大节点构成的大树上连,最后连好的大节点构成的模板树是$O(n)$级别的. 每个

数据结构(树链剖分,堆):HNOI 2016 network

2215. [HNOI2016]网络 ★★★☆   输入文件:network_tenderRun.in   输出文件:network_tenderRun.out   简单对比时间限制:2 s   内存限制:128 MB [题目描述] [输入格式] [输出格式] [样例输入1] 13 23 1 2 1 3 2 4 2 5 3 6 3 7 4 8 4 9 6 10 6 11 7 12 7 13 2 1 0 8 13 3 0 9 12 5 2 9 2 8 2 2 0 10 12 1 2 2 1 3 2

[HNOI 2016]大数

Description 题库链接 给你一个长度为 \(n\) ,可含前导零的大数,以及一个质数 \(p\) . \(m\) 次询问,每次询问你一个大数的子区间 \([l,r]\) ,求出子区间中有多少个子串为 \(p\) 的倍数. \(1\leq n,m\leq 100000\) Solution 记 \(a_i\) 为大数第 \(i\) 位上的数值. 注意到题目是要求 \[\sum_{i=l}^r\sum_{j=i}^r\left[\sum_{k=i}^ja_k\cdot 10^{j-k}\e

[HNOI 2016]最小公倍数

Description 题库链接 给定一张 \(N\) 个顶点 \(M\) 条边的无向图(顶点编号为 \(1,2,\cdots,n\) ),每条边上带有权值.所有权值都可以分解成 \(2^a\times 3^b\) 的形式. \(q\) 个询问,每次询问给定四个参数 \(u,v,a,b\) ,请你求出是否存在一条顶点 \(u\) 到 \(v\) 之间的路径,使得路径依次经过的边上的权值的最小公倍数为 \(2^a\times 3^b\) . \(1\leq n,q\leq 50000,1\leq

《JAVASCRIPT高级程序设计》JSON语法/解析/序列化

JSON是一种数据格式,不是一种编程语言. 一.语法 JSON语法可以表示以下三种类型的值:简单值.对象.数组. 1.简单值 最简单的JSON数据值就是简单值: 5 "hello world" 2.对象 JSON对象与JAVASCRIPT字面量有些不同,以下是javascript中的对象字面量: // 表示方法1 var person = { name:"Lillian", age:24 }; // 表示方法2 var person1 = { "name&

聊一聊前端模板与渲染那些事儿

欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码): https://segmentfault.com/blog/frontenddriver 作为现代应用,ajax的大量使用,使得前端工程师们日常的开发少不了拼装模板,渲染模板.我们今天就来聊聊,拼装与渲染模板的那些事儿. 如果喜欢本文请点击右侧的推荐哦,你的推荐会变为我继续更文的动力 1 页面级的渲染 在刚有web的时候,前端与后端的交互,非常直白,浏览器端发出URL,后端返回一张拼好了的HTML串