1. 程式人生 > 其它 >【題解】[AH2017/HNOI2017]影魔

【題解】[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;
}