【题解】[AH2017/HNOI2017]影魔

Problem

\(\text{Solution:}\)

对于 p1 的获得条件,要求端点两个值恰好是次大值和最大值;对于 p2 的获得条件,要求其中一个是最大值。

线段树并不一定是用来动态直接回答询问的,这题区间的答案也并不好合并。考虑处理出每一个点左右比他大的第一个数的位置后贡献应该长什么样:

首先 R[i] 可以对 L[i] 这个点产生 p1 的贡献。

以 R[i] 为右端点,则所有在 \([L[i]+1,i-1]\) 的点均可以和 R[i] 组合产生 p2 的贡献。

以 L[i] 为左端点,则所有在 \([i+1,R[i]-1]\) 的点均可以和 L[i] 组合产生 p2 的贡献。

于是我们考虑离线扫描:将询问排序后,扫到一个点就把它的贡献加入。我们发现一个点对应的贡献是一段连续区间。线段树就派上用场了。

那么,我们该如何准确获得一个询问的讯息呢?我们需要用扫描到 r 的区间贡献减去扫描到 l-1 的区间贡献,用差分来统计。

所以我们可以将一个询问拆成两个,分别赋值为 1,-1, 来完成差分的效果。

剩下的就是对区间按照横坐标排序,然后扫描加入维护答案即可。我们需要使加贡献的时候横坐标单调递增,所以需要排序。

剩下的就是区间修改和区间求和了。至于 L[i],R[i] 的求法,用单调栈扫一遍就可以线性了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN=5e5+10;
const int inf=(1LL<<60);
int L[MAXN],R[MAXN],st[MAXN],top;
struct Tr {
	int l,r,sum,tag;
} tr[MAXN];
int ls[MAXN],rs[MAXN],node,rt;
int n,a[MAXN],p1,p2,qcnt,pcnt;
int ans[MAXN],m;
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10-48+ch;
		ch=getchar();
	}
	if(w==-1)s=-s;
	return s;
}
struct line {
	int x,l,r,v;
	bool operator<(const line&B)const{
		return x<B.x;
	}
}p[MAXN];
struct Q {
	int x,l,r,v,id;
	bool operator<(const Q&B)const{
		return x<B.x;
	}
}q[MAXN];
inline void pushup(int x) {
	tr[x].sum=tr[ls[x]].sum+tr[rs[x]].sum;
}
inline void build(int &x,int l,int r) {
	x=++node;
	tr[x].l=l;
	tr[x].r=r;
	if(l==r) {
		tr[x].sum=0;
		return;
	}
	int mid=(l+r)>>1;
	build(ls[x],l,mid);
	build(rs[x],mid+1,r);
	pushup(x);
}
inline void pushdown(int x) {
	if(tr[x].tag) {
		int p=tr[x].tag;
		tr[x].tag=0;
		tr[ls[x]].tag+=p;
		tr[rs[x]].tag+=p;
		tr[ls[x]].sum+=(tr[ls[x]].r-tr[ls[x]].l+1)*p;
		tr[rs[x]].sum+=(tr[rs[x]].r-tr[rs[x]].l+1)*p;
	}
}
void change(int x,int l,int r,int v) {
	if(tr[x].l>=l&&tr[x].r<=r) {
		tr[x].sum+=(tr[x].r-tr[x].l+1)*v;
		tr[x].tag+=v;
		return;
	}
	pushdown(x);
	int mid=(tr[x].l+tr[x].r)>>1;
	if(l<=mid)change(ls[x],l,r,v);
	if(mid<r)change(rs[x],l,r,v);
	pushup(x);
}
int query(int x,int l,int r) {
	if(tr[x].l>=l&&tr[x].r<=r) {
		return tr[x].sum;
	}
	pushdown(x);
	int mid=(tr[x].l+tr[x].r)>>1;
	int val=0;
	if(l<=mid)val+=query(ls[x],l,r);
	if(mid<r)val+=query(rs[x],l,r);
	pushup(x);
	return val;
}
signed main() {
	n=read(),m=read();
	p1=read(),p2=read();
	for(int i=1;i<=n;++i)a[i]=read();
	st[top=0]=0;
	for(int i=1;i<=n;++i){
		while(a[i]>a[st[top]]&&top)--top;
		L[i]=st[top];st[++top]=i;
	}
	top=0;
	st[top=0]=n+1;
	for(int i=n;i>=1;--i){
		while(a[i]>a[st[top]]&&top)--top;
		R[i]=st[top];
		st[++top]=i;
	}
	build(rt,0,n+1);
	for(int i=1;i<=m;++i){
		int x,y;
		x=read();y=read();
		ans[i]+=(y-x)*p1;
		q[++qcnt]=(Q){x-1,x,y,-1,i};
		q[++qcnt]=(Q){y,x,y,1,i};
	}
	sort(q+1,q+qcnt+1);
	for(int i=1;i<=n;++i){
		if(L[i]&&R[i]<n+1)p[++pcnt]=(line){R[i],L[i],L[i],p1};
		if(L[i]&&i+1<R[i])p[++pcnt]=(line){L[i],i+1,R[i]-1,p2};
		if(L[i]<i-1&&R[i]<n+1)p[++pcnt]=(line){R[i],L[i]+1,i-1,p2};
	}
	sort(p+1,p+pcnt+1);
	for(int i=1,j=1;i<=qcnt;++i){
		while(j<=pcnt&&p[j].x<=q[i].x){
			change(rt,p[j].l,p[j].r,p[j].v);
			j++;
		}
		ans[q[i].id]+=q[i].v*(query(rt,q[i].l,q[i].r));
	}
	for(int i=1;i<=m;++i)printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-06-25 09:13  Refined_heart  阅读(38)  评论(0编辑  收藏  举报