\(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. mysql数据导入的时候提示Got a packet bigger than 'max_allowed_packet' bytes

    Got a packet bigger than 'max_allowed_packet' bytes错误 默认可能是2M 把max_allowed_packet设置大于5M试试,我设置为160M,输 ...

  2. CUDA 新版本 Visual Studio 和 CUDA 兼容性的小问题

    ▶ 升级到 Visual Studio 2017 和 CUDA 9.1 之后,直接编译以前的 CUDA C 程序出现了如下报错: 严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) E00 ...

  3. Visual Basic 函数速查

    Calendar 常数 可在代码中的任何地方用下列常数代替实际值: 常数 值 描述 vbCalGreg 0 指出使用的是阳历. vbCalHijri 1 指出使用的是伊斯兰历法. Color 常数 可 ...

  4. linux rz sz 的安装

    先wget获取包wget http://freeware.sgi.com/source/rzsz/rzsz-3.48.tar.gz然后解压tar zxvf  rzsz-3.48.tar.gz进入目录c ...

  5. Zabbix 监控 Cisco ASA5525 流量

    简介: Zabbix 监控 Cisco ASA5525 网络接口流量 一.Zabbix 支持 SNMP.Cisco 开启 SNMP 二.测试 shell > snmpwalk -v 2c -c ...

  6. linux命令之scp远程文件复制

    scp是linux中功能最强大的文件传输命令,可以实现从本地到远程以及远程到本地的轻松文件传输操作.下面简单的讲解一些关于scp命令的操作,给有用的人一些参考: 首先是本地到远程的操作:操作的格式如下 ...

  7. InfoPanel

    [InfoPanel] The Info panel shows the color values beneath the pointer and, depending on the tool in ...

  8. cp & scp

    [cp & scp] Linux为我们提供了两个用于文件copy的命令,一个是cp,一个是scp,但是他们略有不同. cp   --- 主要是用于在同一台电脑上,在不同的目录之间来回copy文 ...

  9. msdn 硬盘

    https://msdn.microsoft.com/library/windows/hardware/ff566204 Returns the ATA-2 identify data, the Se ...

  10. wamp如何设置数据库的密码

    WAMP安装好后,MySQL密码是为空的,那么要如何修改呢?其实很简单,通过几条指令就行了,下面我就一步步来操作. 首先,通过WAMP打开mysql控制台. 提示输入密码,因为现在是空,所以直接按回车 ...