P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)
首先我们把这两个贡献翻译成人话:
- 区间 \([l,r]\) 产生 \(p_1\) 的贡献当且仅当 \(a_l,a_r\) 分别为区间 \([l,r]\) 的最大值和次大值。
- 区间 \([l,r]\) 产生 \(p_2\) 的贡献当且仅当 \(a_l\) 为区间 \([l,r]\) 的最大值且 \(a_r\) 不是区间 \([l,r]\) 的次大值,或者 \(a_r\) 为区间 \([l,r]\) 的最大值且 \(a_l\) 不是区间 \([l,r]\) 的次大值。
我们考虑转化贡献体,对于每个区间 \([l,r]\) 分两种情况:
- 若 \(r-l=1\),那么显然所有这样的区间都会产生 \(p_1\) 的贡献,这个我们特判一下即可。
- 若 \(r-l\ge 2\),那么显然对于区间 \([l+1,r-1]\) 有一个唯一的最大值,设其位置为 \(i\),于是我们改枚举 \(i\),看看它会对哪些区间产生贡献。
我们设 \(L_i\) 为在 \(i\) 前面的最靠近 \(i\) 的满足 \(a_j>a_i\) 的 \(j\),\(R_i\) 为在 \(i\) 后面的最靠近 \(i\) 的满足 \(a_j>a_i\) 的 \(j\),这个显然可以一遍单调栈求出,然后分情况讨论:
- 若 \(i\) 为区间 \([l+1,r-1]\) 的最大值,且区间 \([l,r]\) 产生 \(p_1\) 的贡献,那显然只能是 \(l=L_i,r=R_i\),因为如果左端点 \(l>L_i\) 那 \(a_l\) 就不是 \([l,r]\) 的较大值(或次大值)了(因为 \(a_l<a_i\)),如果左端点 \(l<L_i\) 那 \(a_i\) 就不是 \([l+1,r-1]\) 的最大值了(因为 \(a_{L_i}>a_i\));右端点同理。
- 若 \(i\) 为区间 \([l+1,r-1]\) 的最大值,且区间 \([l,r]\) 产生 \(p_2\) 的贡献,那我们分最大值在左端点处和最大值在右端点处两种情况。这里以最大值在左端点处为例,显然 \(l=L_i\),而右端点理论上来说可以取遍 \((L_i,R_i)\) 中所有值,而我们强制 \(i\) 为区间 \([l+1,r-1]\) 的最大值,故 \(r\in(i,R_i)\)。也就是说 \(l=L_i,r\in(i,R_i)\),另一半同理可得 \(r=R_i,l\in(L_i,i)\)。
考虑借鉴 P5445 [APIO2019]路灯 的套路,建立二维平面直角坐标系,点 \((i,j)\) 表示以 \(i,j\) 为端点的区间的贡献,那么显然对于每组询问我们只需求出以 \((l,l)\) 左下角,\((r,r)\) 为右下角的矩形中所有数的和。而显然上面的贡献都可转化为”纵坐标为 \(y\),横坐标在区间 \([l,r]\) 中的点的贡献增加 \(v\) 的形式“。那么显然我们可以把询问进行差分处理,并离线扫描线+线段树回答每个询问,复杂度线对。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=2e5;
int n,m,qu=0,p1,p2,a[MAXN+5],L[MAXN+5],R[MAXN+5];
struct node{int l,r;ll sum,lz;} s[MAXN*4+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;
int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(s[k].lz){
s[k<<1].sum+=1ll*(s[k<<1].r-s[k<<1].l+1)*s[k].lz;s[k<<1].lz+=s[k].lz;
s[k<<1|1].sum+=1ll*(s[k<<1|1].r-s[k<<1|1].l+1)*s[k].lz;s[k<<1|1].lz+=s[k].lz;
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].sum+=1ll*x*(s[k].r-s[k].l+1);
s[k].lz+=x;return;
} pushdown(k);int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
s[k].sum=s[k<<1].sum+s[k<<1|1].sum;
}
ll query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].sum;
pushdown(k);int mid=(s[k].l+s[k].r)>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
}
vector<pair<pii,int> > add[MAXN+5];
stack<pii> stk;ll ans[MAXN+5];
struct query{
int x,l,r,p,t;
bool operator <(const query &rhs){return x<rhs.x;}
} q[MAXN*2+5];
int main(){
scanf("%d%d%d%d",&n,&m,&p1,&p2);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
while(!stk.empty()&&stk.top().fi<a[i]) R[stk.top().se]=i,stk.pop();
stk.push(mp(a[i],i));
} while(!stk.empty()) R[stk.top().se]=n+1,stk.pop();
for(int i=n;i;i--){
while(!stk.empty()&&stk.top().fi<a[i]) L[stk.top().se]=i,stk.pop();
stk.push(mp(a[i],i));
} while(!stk.empty()) L[stk.top().se]=0,stk.pop();
for(int i=1;i<=n;i++){
if(L[i]&&R[i]!=n+1) add[L[i]].pb(mp(mp(R[i],R[i]),p1));
if(L[i]&&R[i]!=i+1) add[L[i]].pb(mp(mp(i+1,R[i]-1),p2));
if(R[i]!=n+1&&L[i]!=i-1) add[R[i]].pb(mp(mp(L[i]+1,i-1),p2));
}
for(int i=1;i<=m;i++){
int l,r;scanf("%d%d",&l,&r);ans[i]+=1ll*(r-l)*p1;
q[++qu]={r,l,r,i,1};q[++qu]={l-1,l,r,i,-1};
} build(1,1,n);
sort(q+1,q+qu+1);int cur=1;
for(int i=1;i<=qu;i++){
while(cur<=q[i].x){
ffe(it,add[cur]) modify(1,it->fi.fi,it->fi.se,it->se);
cur++;
} ans[q[i].p]+=q[i].t*query(1,q[i].l,q[i].r);
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
P3722 [AH2017/HNOI2017]影魔(单调栈+扫描线+线段树)的更多相关文章
- 【BZOJ4826】[Hnoi2017]影魔 单调栈+扫描线
[BZOJ4826][Hnoi2017]影魔 Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝 ...
- Codeforces 407E - k-d-sequence(单调栈+扫描线+线段树)
Codeforces 题面传送门 & 洛谷题面传送门 深感自己线段树学得不扎实-- 首先特判掉 \(d=0\) 的情况,显然这种情况下满足条件的区间 \([l,r]\) 中的数必须相同,双针扫 ...
- 【bzoj4826】[Hnoi2017]影魔 单调栈+可持久化线段树
题目描述 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵魂,都有着自己 ...
- BZOJ 4826: [Hnoi2017]影魔 单调栈+可持久化线段树
Description 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样 的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个 ...
- 洛谷P3722 [AH2017/HNOI2017]影魔(线段树)
题意 题目链接 Sol 题解好神仙啊qwq. 一般看到这种考虑最大值的贡献的题目不难想到单调数据结构 对于本题而言,我们可以预处理出每个位置左边第一个比他大的位置\(l_i\)以及右边第一个比他大的位 ...
- [BZOJ4826] [HNOI2017] 影魔 单调栈 主席树
题面 因为是一个排列,所以不会有重复的.如果有重复就没法做了.一开始没有仔细看题目想了半天. 发现,如果是第一种情况,那么边界\(l\)和\(r\)就应该分别是整个区间的最大值和次大值. 然后,对于那 ...
- BZOJ 4826: [Hnoi2017]影魔 单调栈 主席树
https://www.lydsy.com/JudgeOnline/problem.php?id=4826 年少不知空间贵,相顾mle空流泪. 和上一道主席树求的东西差不多,求两种对 1. max(a ...
- 51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线
区间计数 基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80 两个数列 {An} , {Bn} ,请求出Ans, Ans定义如下: Ans:=Σni=1Σnj=i[max{ ...
- 有趣的线段树模板合集(线段树,最短/长路,单调栈,线段树合并,线段树分裂,树上差分,Tarjan-LCA,势能线段树,李超线段树)
线段树分裂 以某个键值为中点将线段树分裂成左右两部分,应该类似Treap的分裂吧(我菜不会Treap).一般应用于区间排序. 方法很简单,就是把分裂之后的两棵树的重复的\(\log\)个节点新建出来, ...
随机推荐
- javascript-jquery介绍
jquery优势 1.轻量级 2.强大的选择器 3.出色的DOM封装 4.可靠的事件处理机制 5.完善的Ajax 6.不污染顶级变量 7.出色的浏览器兼容 8.链式操作方式 9.隐式迭代 10.行为层 ...
- Python设置Excel样式
前面已经详细讲解过使用Python对Excel表格进行读.写操作,本文主要讲解下使用Python设置Excel表格的样式. 深入学习请参考openpyxl官方文档: https://openpyxl. ...
- 【二食堂】Alpha - Scrum Meeting 8
Scrum Meeting 8 例会时间:4.18 11:40 - 12:10 进度情况 组员 昨日进度 今日任务 李健 1. 实体的添加和关系的添加实现的有bug,柴博和刘阳进行了帮助issue 1 ...
- oo第一次博客-三次表达式求导的总结与反思
一.问题回顾与基本设计思路 三次作业依次是多项式表达式求导,多项式.三角函数混合求导,基于三角函数和多项式的嵌套表达式求导. 第一次作业想法很简单,根据指导书,我们可以发现表达式是由各个项与项之间的运 ...
- spring security整合QQ登录
最近在了解第三方登录的内容,尝试对接了一下QQ登录,此次记录一下如何实现QQ登录的过程,在这个例子中是和spring secuirty整合的,不整合spring secuirty也是一样的. 需求: ...
- 大厂面试题系列:重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分
面试题:重载(Overload)和重写(Override)的区别.重载的方法能否根据返回类型进行区分 面试官考察点猜想 这道题纯粹只是考查基础理论知识,对实际开发工作中没有太多的指导意义,毕竟编辑器都 ...
- stm32电机控制之控制两路直流电机
小车使用的电机是12v供电的直流电机,带编码器反馈,这样就可以采用闭环速度控制,这里电机使用PWM驱动,速度控制框图如下: 由以上框图可知,STM32通过定时器模块输出PWM波来控制两个直流电机的转动 ...
- Python课程笔记(九)
本次课程主要学习了Excel和JSON格式的一些读写操作.课程代码 一.Excel数据读写操作 1.安装模块 pip install xlrd pip install xlwt 网不好可以采用三方库: ...
- 翻转子串 牛客网 程序员面试金典 C++ Python
反转子串 牛客网 程序员面试金典 C++ Python 题目描述 假定我们都知道非常高效的算法来检查一个单词是否为其他字符串的子串.请将这个算法编写成一个函数,给定两个字符串s1和s2,请编写代码检查 ...
- 什么是 Webhook?
1. 什么是 Webhook? Webhook 是一个 API 概念,是微服务 API 的使用范式之一,也被成为反向 API,即前端不主动发送请求,完全由后端推送:举个常用例子,比如你的好友发了一条朋 ...