好题啊
先把两种贡献翻译成人话
对于((i,j)),如果(a_i,a_j)恰好是([i,j])之间的最大值和次大值,那么((i,j))产生(p1)的贡献
否则对于((i,j)),(a_i)是最大值或者(a_j)是最大值,那么就产生(p2)的贡献
哎这个一个最大值一个严格次大值好像有点眼熟啊
这不bzoj原题吗
根据那道题我们发现的性质,能产生(p1)贡献的点对不会超过(2n)个,于是我们用单调栈将这些点对都预处理出来,之后树状数组+扫描线就可以知道每个询问有多少个(p1)的贡献
现在求出(p2)的贡献就好了
我们考虑一个元素往左右两边扩展,扩展到比它大的元素就停止,这些扫到的点显然都会产生贡献,但是这个点对产生的贡献是(p1)还是(p2)就不好算了
但是我们都已经算出来(p1)的点对数量了,只要拿总数量减一下就是(p2)点对的数量了
显然每一个位置往左往右扩展到哪里单调栈已经帮我们求好了,所以现在的问题变成了求每一个询问区间内,每一个位置在区间内往左往右扩展的总长度与之和
考虑把这个扩展的总长度变成一个区间加法,于是我们可以把每一个元素的扩展搞成一个三元组((l,r,k)),扩展到([l,r]),扩展的位置是(k)
我们把每个询问拆成两个,之后将这些区间按照(k)来排序,线段树+扫描线就可以了
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lowbit(x) ((x)&(-x))
#define LL long long
#define re register
#define maxn 200005
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct Seg{int x,y,k;}t[maxn<<1];
struct node{int x,y;}p[maxn<<1];
struct Ask{int l,r,rk;}q[maxn];
struct _Ask{int x,rk,o,l,r;}_q[maxn<<1];
int n,m,p1,p2,top,tot;
int l[maxn],r[maxn],a[maxn],st[maxn],c[maxn];
int ls[maxn<<2],rs[maxn<<2],tag[maxn<<2];LL d[maxn<<2];
int Ans1[maxn];LL Ans2[maxn];
inline int cxp(_Ask A,_Ask B) {return A.x<B.x;}
inline int cmp(Ask A,Ask B) {return A.r<B.r;}
inline int cop(node A,node B) {return A.y<B.y;}
inline void add(int x,int val) {for(re int i=x;i<=n;i+=lowbit(i)) c[i]+=val;}
inline int ask(int x) {int cnt=0;for(re int i=x;i;i-=lowbit(i)) cnt+=c[i];return cnt;}
void build(int x,int y,int i) {
ls[i]=x,rs[i]=y;
if(x==y) return;
int mid=x+y>>1;
build(x,mid,i<<1),build(mid+1,y,i<<1|1);
}
inline void pushdown(int i) {
if(!tag[i]) return;
tag[i<<1]+=tag[i],tag[i<<1|1]+=tag[i];
d[i<<1]+=tag[i]*(rs[i<<1]-ls[i<<1]+1);
d[i<<1|1]+=tag[i]*(rs[i<<1|1]-ls[i<<1|1]+1);
tag[i]=0;
}
void change(int x,int y,int i) {
if(x<=ls[i]&&y>=rs[i]) {
tag[i]++;
d[i]+=rs[i]-ls[i]+1;
return;
}
pushdown(i);
int mid=ls[i]+rs[i]>>1;
if(y<=mid) change(x,y,i<<1);
else if(x>mid) change(x,y,i<<1|1);
else change(x,y,i<<1|1),change(x,y,i<<1);
d[i]=d[i<<1|1]+d[i<<1];
}
int query(int x,int y,int i) {
if(x<=ls[i]&&y>=rs[i]) return d[i];
pushdown(i);
int mid=ls[i]+rs[i]>>1;
if(y<=mid) return query(x,y,i<<1);
if(x>mid) return query(x,y,i<<1|1);
return query(x,y,i<<1|1)+query(x,y,i<<1);
}
signed main() {
n=read(),m=read();p1=read(),p2=read();
for(re int i=1;i<=n;i++) a[i]=read();
for(re int i=1;i<=n;i++) {
while(top&&a[st[top]]<a[i]) r[st[top--]]=i;
st[++top]=i;
}
while(top) r[st[top--]]=n+1;
for(re int i=n;i;--i) {
while(top&&a[st[top]]<a[i]) l[st[top--]]=i;
st[++top]=i;
}
while(top) l[st[top--]]=0;
for(re int i=1;i<=n;i++) {
if(l[i]) p[++tot]=(node){l[i],i};
if(r[i]!=n+1) p[++tot]=(node){i,r[i]};
}
for(re int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].rk=i;
std::sort(p+1,p+tot+1,cop),std::sort(q+1,q+m+1,cmp);
top=1;int now=1;
for(re int i=1;i<=n;i++) {
while(now<=tot&&p[now].y<=i) add(p[now].x,1),now++;
while(top<=m&&q[top].r<=i)
Ans1[q[top].rk]=ask(q[top].r)-ask(q[top].l-1),top++;
}
tot=0;now=0;build(1,n,1);
for(re int i=1;i<=n;i++) {
if(l[i]+1<=i-1) t[++tot]=(Seg){l[i]+1,i-1,i};
if(i+1<=r[i]-1) t[++tot]=(Seg){i+1,r[i]-1,i};
}
for(re int i=1;i<=m;i++)
_q[++now]=(_Ask){q[i].l-1,q[i].rk,-1,q[i].l,q[i].r},_q[++now]=(_Ask){q[i].r,q[i].rk,1,q[i].l,q[i].r};
std::sort(_q+1,_q+now+1,cxp);
int tmp=1,cnt=1;
for(re int i=0;i<=n;i++) {
while(tmp<=tot&&t[tmp].k<=i) change(t[tmp].x,t[tmp].y,1),tmp++;
while(cnt<=now&&_q[cnt].x<=i) Ans2[_q[cnt].rk]+=_q[cnt].o*query(_q[cnt].l,_q[cnt].r,1),cnt++;
}
for(re int i=1;i<=m;i++)
printf("%lld
",(LL)p1*Ans1[i]+(LL)p2*(Ans2[i]-Ans1[i]));
return 0;
}