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\)个节点新建出来, ...
随机推荐
- Golang通脉之数据类型
标识符与关键字 在了解数据类型之前,先了解一下go的标识符和关键字 标识符 在编程语言中标识符就是定义的具有某种意义的词,比如变量名.常量名.函数名等等. Go语言中标识符允许由字母数字和_(下划线) ...
- 初学python-day9 函数1(已更新)
函数 一.函数基础 1.什么是函数 在一个完整的项目中,某些功能会被重复使用,那么会将代码段封装成函数,当我们要使用的时候,直接调用即可. 函数是可以实现一定的小程序或者功能. 优点: 增加了代码的重 ...
- 【UE4】虚幻引擎技术直播汇总(含中英文直播)
B站虚幻引擎官方账号 中文直播 [中文直播]第35期 | 使用GIS在UE中创造真实地球风貌 | Epic 周澄清 [中文直播]第34期 | 包教包会的Epic MegaGrants申请之道 | Ep ...
- [软工顶级理解组] Alpha阶段事后分析
目录 设想和目标 计划 资源 变更管理 设计/实现 测试/发布 团队的角色,管理,合作 总结 质量提高 会议截图 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰 ...
- Spring Security Resource Server的使用
Spring Security Resource Server的使用 一.背景 二.需求 三.分析 四.资源服务器认证流程 五.实现资源服务器 1.引入jar包 2.资源服务器配置 3.资源 六.测试 ...
- 搬运1:关于对C语言中数组名取地址加减等操作的一点探究
对于数组名取地址强制转换的操作 偶然在晚上学了C语言指针后网页闲逛找题时,被一个数组名取地址搞糊涂了,在自己试验加探索后我稍微悟了一点东西. 代码如下: #include<stdio.h> ...
- 编译qwt遇到的问题
在windows下使用mingw编译从git上下载的qwt工程下的tests时一直提示一下错误: error: undefined reference to `qMain(int, char**)' ...
- hdu 5055 Bob and math problem (很简单贪心)
给N个数字(0-9),让你组成一个数. 要求:1.这个数是奇数 2.这个数没有前导0 问这个数最大是多少. 思路&解法: N个数字从大到小排序,将最小的奇数与最后一位交换,把剩下前N-1位从大 ...
- MySQL到底能否解决幻读问题
先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的. 方法:是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作:2. ...
- 【浏览器】聊聊DOM
[浏览器]聊聊DOM 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 作为前端开发,在以前的工作中大多是和DOM打交道,到 ...