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\)个节点新建出来, ...
随机推荐
- [转载]CSS3实现文本垂直排列
最近的一个项目中要使文字垂直排列,也就是运用了CSS的writing-mode属性. writing-mode最初时ie中支持的一个属性,后来在CSS3中增添了这一新的属性,所以在ie中和其他浏览器中 ...
- JBOSS未授权访问漏洞利用
1. 环境搭建 https://www.cnblogs.com/chengNo1/p/14297387.html 搭建好vulhub平台后 进入对应漏洞目录 cd vulhub/jboss/CVE-2 ...
- 安装pytorch的细节记录
1.根据教程安装pytorch的时候发现太慢了,无法容忍,根据https://blog.csdn.net/zzq060143/article/details/88042075z在Ancona Prom ...
- Qt字符编码小知识
1.VS2010默认编码是GBK,Qt5的内置编码是utf-8,想要在VS2010及其以上版本,优雅的使用utf-8的字符编码需要 // Coding: UTF-8(BOM) #if defined( ...
- Shadertoy 教程 Part 2 - 圆和动画
Note: This series blog was translated from Nathan Vaughn's Shaders Language Tutorial and has been au ...
- 把二叉树打印成多行 牛客网 剑指Offer
把二叉树打印成多行 牛客网 剑指Offer 题目描述 从上到下按层打印二叉树,同一层结点从左至右输出.每一层输出一行 # class TreeNode: # def __init__(self, x) ...
- 高度最小的BST 牛客网 程序员面试金典 C++ Python
高度最小的BST 牛客网 程序员面试金典 C++ Python 题目描述 对于一个元素各不相同且按升序排列的有序序列,请编写一个算法,创建一棵高度最小的二叉查找树. 给定一个有序序列int[] val ...
- linux 内核源代码情景分析——linux 内存管理的基本框架
386 CPU中的页式存管的基本思路是:通过页面目录和页面表分两个层次实现从线性地址到物理地址的映射.这种映射模式在大多数情况下可以节省页面表所占用的空间.因为大多数进程不会用到整个虚存空间,在虚存空 ...
- echarts 让轴自适应数据为小数整数
echarts 让轴自适应数据为小数整数,以解决y轴数值重复的问题 工作中突然遇到这个问题 试了一下用formatter自适应 ok 在yAxis中提阿尼按键属性 axisLabel 1 axis ...
- ADB WIFI无线调试真正摆脱usb数据线连接,一次也不用!
常见的使用ADB无线调试步骤 手机"开发者模式"菜单中开启"USB调试" 和"无线调试",手机网络与电脑在同一网内; 手机使用USB与电脑进 ...