[BZOJ4826] [HNOI2017] 影魔 单调栈 主席树

题面

因为是一个排列,所以不会有重复的。如果有重复就没法做了。一开始没有仔细看题目想了半天。

发现,如果是第一种情况,那么边界\(l\)\(r\)就应该分别是整个区间的最大值和次大值。

然后,对于那第二种情况, \(l\)\(r\)中,只有一个数是最大值,另一个数不可以是最大值和次大值。

于是我们考虑从每一个合法区间内数里面选出一个代表来可以直接代表整个区间。

用单调栈维护一下\(lp[i]\)\(rp[i]\)分别表示一个数左边和右边离\(i\)最近的大于之的数。

然后对于第一种情况,发现相邻两个数一定是对的。然后发现,任何一个\([lp[i],rp[i]]\)也是对的。因为可以保证边界上的数的最大与次大的性质。

那么第二种呢?只要保证一个边界最大即可,因此就是\([lp[i]+1..i-1,rp[i]]\)\([lp[i],i+1..rp[i]-1]\)。可以保证不会重复,因为这里\(i\)实际上是每个区间的次大值,每个区间只会被其次大值统计一次。

于是我们考虑把一个区间的边界位置分别作为二维的坐标,发现问题就变成了求以\(l,l\)\(r,r\)为顶点的正方形内的和。

显然可以用扫描线。但是其实可以更方便。每一个位置只能是一个线段,所以我们直接用主席树维护一下即可。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define REP(i,a,n) for(register int i(a);i<=(n);++i)
#define PER(i,a,n) for(register int i(a);i>=(n);--i)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
const int SZ=(1<<21)+1;char ibuf[SZ],*iS,*iT,obuf[SZ+128],*oS=obuf,*oT=obuf+SZ-1;
#ifndef ONLINE_JUDGE
#define gc() getchar()
#else
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SZ,stdin),iS==iT?EOF:*iS++):*iS++)
#endif
template<typename I>inline void read(I&x){char c=gc();int f=1;for(;c<'0'||c>'9';c=gc())c=='-'?f=-1:0;for(x=0;c>='0'&&c<='9';c=gc())x=(x<<1)+(x<<3)+(c&15);f==-1?x=-x:0;}
inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
#define printf(...) oS>oT&&(flush(),1),oS+=sprintf(oS,__VA_ARGS__)
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline char SMAX(A&a,const B&b){return a<b?a=b,1:0;}
template<typename A,typename B>inline char SMIN(A&a,const B&b){return a>b?a=b,1:0;}

const int N=200000+7;
int n,m,T,p1,p2,a[N],x,y,q[N],tl,lp[N],rp[N];

struct Node{int lc,rc,lps;ll val,add;}t[N*50];int RT[N],nod;//错误笔记:这里不是单调修改,所以仅仅开LOG被是不够的 
inline void Insert(int &o,int L,int R,int l,int r,int k){
	if(t[o].lps<T)t[++nod]=t[o],o=nod,t[o].lps=T;
	if(l<=L&&R<=r)return (void)(t[o].add+=k,t[o].val+=(ll)k*(R-L+1));
	int M=(L+R)>>1;if(l<=M)Insert(t[o].lc,L,M,l,r,k);if(r>M)Insert(t[o].rc,M+1,R,l,r,k);
	t[o].val=t[t[o].lc].val+t[t[o].rc].val+t[o].add*(R-L+1);
}
inline ll Query(int o,int p,int L,int R,int l,int r,ll adv){
	if(l<=L&&R<=r)return t[o].val-t[p].val+adv*(R-L+1);
	int M=(L+R)>>1;if(r<=M)return Query(t[o].lc,t[p].lc,L,M,l,r,adv+t[o].add-t[p].add);else if(l>M)return Query(t[o].rc,t[p].rc,M+1,R,l,r,adv+t[o].add-t[p].add);
	else return Query(t[o].lc,t[p].lc,L,M,l,r,adv+t[o].add-t[p].add)+Query(t[o].rc,t[p].rc,M+1,R,l,r,adv+t[o].add-t[p].add);
}

struct Pair{int x,y,w;};std::vector<Pair>g[N];
inline void Preprocess(){
	REP(i,1,n){
		while(tl&&a[q[tl]]<a[i])--tl;
		q[++tl]=i;lp[i]=q[tl-1];
	}q[tl=0]=n+1;PER(i,n,1){
		while(tl&&a[q[tl]]<a[i])--tl;
		q[++tl]=i;rp[i]=q[tl-1];
		if(i<n)g[i].push_back(Pair{i+1,i+1,p1});
		if(lp[i]&&rp[i]<=n)g[rp[i]].push_back(Pair{lp[i],lp[i],p1});
		if(rp[i]<=n&&i-1>=lp[i]+1)g[rp[i]].push_back(Pair{lp[i]+1,i-1,p2});
		if(lp[i]&&i+1<=rp[i]-1)g[lp[i]].push_back(Pair{i+1,rp[i]-1,p2});
	}
	REP(i,1,n){
		T=i;RT[i]=RT[i-1];int len=g[i].size();
		REP(j,0,len-1)Insert(RT[i],1,n,g[i][j].x,g[i][j].y,g[i][j].w);
	}
}

int main(){
	read(n);read(m),read(p1),read(p2);
	REP(i,1,n)read(a[i]);
	Preprocess();
	REP(i,1,m){
		read(x),read(y);
		printf("%lld\n",Query(RT[y],RT[x-1],1,n,x,y,0));
	}return flush(),0;
}
posted @ 2018-11-16 19:55  hankeke303  阅读(113)  评论(0编辑  收藏  举报