\(Description\)

给你一个序列,每次询问一个区间,求其所有子区间的最小值之和

\(Solution\)

这里要用莫队算法

首先令\(val\)数组为原序列

我们考虑怎么由一个区间\([l,r]\)到\([l,r+1]\)

我们发现新增加的区间为:

\[[l,r+1],[l+1,r+1],[l+2,r+1]...[r,r+1],[r+1,r+1]
\]

我们令\([l,r+1]\)内的最小值的位置为\(x\)

则\([l,r+1],[l+1,r+1]...[x-1,r+1],[x,r+1]\)的最小值都为\(val[x]\).

所以现在我们只需要考虑\([x+1,r+1],[x+2,r+1]...[r,r+1],[r+1,r+1]\)区间对答案的影响即可

用单调栈处理出\(pre[i]\)

\(pre[i]\)表示在\(i\)前第一个小于他的数的位置

这样子就可以知道左端点在\([pre_i,i]\)之间的数时,最小值都为\(i\)

求出这个就可以求出来\([x+1,r+1],[x+2,r+1]...[r,r+1],[r+1,r+1]\)的答案了

代码为:

for(int i=1;i<=n;i++) suml[i]=suml[pre[i]]+c[i]*(i-pre[i]);

有点类似前缀和,询问也差不多

具体直接见代码吧

\(Code\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int mod=1e9+7;
int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x*f;
}
struct node {
int l,r,id,ans;
}a[100010];
int block[100010],c[500001],lg[500001];
bool cmp1(const node & a , const node & b ){
return block[a.l]==block[b.l]?a.r<b.r:a.l<b.l;
}
bool cmp2(const node & a , const node & b ){
return a.id<b.id;
}
int p,m,sqr,n,f[100010][60],suml[500001],sumr[500001];
int pre[500001],nex[500001];
stack<int> s;
int min(int a,int b){
return c[a]>c[b]?b:a;
}
void init() {
for(int i=1; i<=n; i++)
f[i][0]=i;
for(int i=1; i<=lg[n]; i++)
for(int j=1; j<=n-(1<<i)+1; j++)
f[j][i]=min(f[j][i-1],f[j+(1<<(i-1))][i-1]);
for(int i=1;i<=n;i++){
while(!s.empty()&&c[s.top()]>c[i]) s.pop();
if(s.empty())
pre[i]=0,s.push(i);
else
pre[i]=s.top(),s.push(i);
}
while(!s.empty()){
p=s.top(),s.pop();
if(s.empty())
pre[p]=0;
else
pre[p]=s.top();
}
for(int i=n;i>=1;i--){
while(!s.empty()&&c[s.top()]>c[i]) s.pop();
if(s.empty())
nex[i]=n+1,s.push(i);
else
nex[i]=s.top(),s.push(i);
}
while(!s.empty()){
p=s.top(),s.pop();
if(s.empty())
nex[p]=n+1;
else
nex[p]=s.top();
}
for(int i=1;i<=n;i++)
suml[i]=suml[pre[i]]+c[i]*(i-pre[i]);
for(int i=n;i>=1;i--)
sumr[i]=sumr[nex[i]]+c[i]*(nex[i]-i);
}
int find(int a,int b) {
int k=lg[b-a+1];
return min(f[a][k],f[b-(1<<k)+1][k]);
}
int work(int l,int r){
int p=find(l,r);
return c[p]*(r-p+1)+sumr[l]-sumr[p];
}
int solve(int l,int r){
int p=find(l,r);
return c[p]*(p-l+1)+suml[r]-suml[p];
}
main(){
n=read(),m=read(),sqr=sqrt(n);
for(int i=1;i<=n;i++)
scanf("%lld",&c[i]),block[i]=i/sqr+1,lg[i]=log(i)/log(2);
for(int i=1;i<=m;i++)
scanf("%lld%lld",&a[i].l,&a[i].r),a[i].id=i;
sort(a+1,a+1+m,cmp1);
init();
int l=1,r=0,ans=0;
for(int i=1;i<=m;i++){
int x=a[i].l,y=a[i].r;
while(r<y) ans+=solve(l,r+1),r++;
while(l>x) ans+=work(l-1,r),l--;
while(r>y) ans-=solve(l,r),r--;
while(l<x) ans-=work(l,r),l++;
a[i].ans=ans;
}
sort(a+1,a+1+m,cmp2);
for(int i=1;i<=m;i++)
cout<<a[i].ans<<endl;
}

「HNOI 2016」 序列的更多相关文章

  1. LOJ#3054. 「HNOI 2019」鱼

    LOJ#3054. 「HNOI 2019」鱼 https://loj.ac/problem/3054 题意 平面上有n个点,问能组成几个六个点的鱼.(n<=1000) 分析 鱼题,劲啊. 容易想 ...

  2. 【HNOI 2016】序列

    Problem Description 给定长度为 \(n\) 的序列:\(a_1, a_2, \cdots , a_n\),记为 \(a[1 \colon n]\).类似地,\(a[l \colon ...

  3. 「HNOI 2015」实验比较

    \(Description\) 有\(n\)个元素,对于每个元素\(x_i\)最多知道一个形如\(x_j < x_i\)或\(x_j=x_i\)的条件,问有多少合法的序列.合法的序列满足每个元素 ...

  4. loj 2292「THUSC 2016」成绩单

    loj 看着就很区间dp,所以考虑求\(f_{i,j}\)表示区间\([i,j]\)的答案.注意到贡献答案的方式是每次选一个连续段,拿走后剩下的段拼起来继续段,所以转移就考虑从最后一次选的方法转移过来 ...

  5. Solution -「APIO 2016」「洛谷 P3643」划艇

    \(\mathcal{Description}\)   Link & 双倍经验.   给定 \(n\) 个区间 \([a_i,b_i)\)(注意原题是闭区间,这里只为方便后文描述),求 \(\ ...

  6. 「HNOI 2019」白兔之舞

    一道清真的数论题 LOJ #3058 Luogu P5293 题解 考虑$ n=1$的时候怎么做 设$ s$为转移的方案数 设答案多项式为$\sum\limits_{i=0}^L (sx)^i\bin ...

  7. LOJ 2292 「THUSC 2016」成绩单——区间DP

    题目:https://loj.ac/problem/2292 直接 DP 很难做,主要是有那种 “一个区间内部有很多个别的区间” 的情况. 自己想了一番枚举 max-min 的最大限制,然后在该基础上 ...

  8. LOJ 2991 「THUSC 2016」补退选——trie+线段树合并或vector

    题目:https://loj.ac/problem/2291 想了线段树合并的做法.就是用线段树维护 trie 的每个点在各种时间的操作. 然后线段树合并一番,线段树维护前缀最大值,就是维护最大子段和 ...

  9. 2018.10.27 loj#2292. 「THUSC 2016」成绩单(区间dp)

    传送门 g[i][j][k][l]g[i][j][k][l]g[i][j][k][l]表示将区间l,rl,rl,r变成最小值等于kkk,最大值等于lll时的花费的最优值. f[i][j]f[i][j] ...

随机推荐

  1. C# Panel控件截图

    [System.Runtime.InteropServices.DllImportAttribute("gdi32.dll ")] private static extern bo ...

  2. LevelDb日知录之五:MemTable详解

    [LevelDb日知录之五:MemTable详解] LevelDb日知录前述小节大致讲述了磁盘文件相关的重要静态结构,本小节讲述内存中的数据结构Memtable,Memtable在整个体系中的重要地位 ...

  3. 什么是2MSL

    [什么是2MSL] MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃.因为tcp报文( ...

  4. 向值栈放List集合

    ------------------siwuxie095 向值栈放 List 集合 1.具体步骤 (1)在 Action 中定义 List 集合对象 (2)提供 List 集合对象的 get 方法 ( ...

  5. [Selenium]Grid模式下运行时打印出当前Case在哪台node机器上运行

    当Case在本地运行成功,在Grid模式下运行失败时,我们需要在Grid模式下进行调试,同时登录远程的node去查看运行的情况. Hub是随机将case分配到某台node上运行的,怎样知道当前的cas ...

  6. Zookeeper 源码(二)序列化组件 Jute

    Zookeeper 源码(二)序列化组件 Jute 一.序列化组件 Jute 对于一个网络通信,首先需要解决的就是对数据的序列化和反序列化处理,在 ZooKeeper 中,使用了Jute 这一序列化组 ...

  7. jsoncpp学习

    // MyJsonTest.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <fstream> #includ ...

  8. CTF中的变量覆盖漏洞

    https://www.cnblogs.com/bmjoker/p/9025351.html 原   作 者:bmjoker出 处:https://www.cnblogs.com/bmjoker/p/ ...

  9. 在Eclipse中使用Struts和Hibernate框架搭建Maven Web项目

    前言 学习使用Java还是2012年的事情,刚开始学习的Java的时候,使用的是MyEclipse工具和SSH框架.初学者适合使用MyEclipse,因为他将struts.Spring和Hiberna ...

  10. [转]Android下怎么使用LDD查看依赖库

    Android下没有ldd可以使用,在进行ndk开发的时候,检查库的依赖项特别麻烦.有两个解决方案: 1.将linux的的ldd移植过去.因为android也是基于linux的,所以将ldd移植过去是 ...