题目描述
给定一个长度
n≤2×105
的序列a,对于点对
(i,j)
若不存在
i<k<j
使得
a[i]<a[k]或a[j]<a[k]
,则点对向答案贡献
p1
,若存在
i<k<j
使得
a[i]<a[k]且a[k]<a[j]
,或
a[i]>a[k]且a[k]>a[j]
时,点对向答案贡献
p2
现在有
m≤2×105
个询问,每次查询区间
[a,b]
中的点对向答案的贡献之和。
题目解析
首先,我们的第一想法就枚举每一个位置作为
k
,看左边有多少个小于/大于,右边有多少个小于/大于
又因为我们对于每一个询问都需要回答,于是我们便可以将贡献点对映射到
x,y
轴上,用主席树维护区间增加,区间查询,至于在
x
轴上区间加可以转换为在另一棵树上
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 200000
#define MAXLOG 18
#define MAXM 200000
#define INF 0x3f3f3f3f
typedef long long int LL;
template<class T>
void Read(T &x){
x=0;char c=getchar();bool flag=0;
while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
if(flag)x=-x;
}
struct node{
LL val,lazy;
int ls,rs;
}Leaf[MAXM*MAXLOG*5+100];
int New_Leaf=0;
int NewNode(){
int x=++New_Leaf;
Leaf[x].ls=Leaf[x].rs=Leaf[x].val=Leaf[x].lazy=0;
return x;
}
void insert(int ll,int rr,int l,int r,int w,int &x,int last){
x=NewNode();
Leaf[x]=Leaf[last];
if(ll==l&&rr==r){
Leaf[x].lazy+=w;
return;
}
else{
Leaf[x].val+=1ll*w*(r-l+1);
int mid=(ll+rr)>>1;
if(r<=mid)insert(ll,mid,l,r,w,Leaf[x].ls,Leaf[last].ls);
else if(mid<l)insert(mid+1,rr,l,r,w,Leaf[x].rs,Leaf[last].rs);
else insert(ll,mid,l,mid,w,Leaf[x].ls,Leaf[last].ls),
insert(mid+1,rr,mid+1,r,w,Leaf[x].rs,Leaf[last].rs);
}
}
LL query(int ll,int rr,int l,int r,int x){
if(!x)return 0;
if(l<=ll&&rr<=r)
return Leaf[x].val+Leaf[x].lazy*(rr-ll+1);
else{
int mid=(ll+rr)>>1;
if(r<=mid)return Leaf[x].lazy*(r-l+1)+query(ll,mid,l,r,Leaf[x].ls);
else if(mid<l)return Leaf[x].lazy*(r-l+1)+query(mid+1,rr,l,r,Leaf[x].rs);
else return Leaf[x].lazy*(r-l+1)+query(ll,mid,l,mid,Leaf[x].ls)+query(mid+1,rr,mid+1,r,Leaf[x].rs);
}
}
int root1[MAXN+10],root2[MAXN+10];
int A[MAXN+10];
int l[MAXN+10],r[MAXN+10];
int n,m,p1,p2;
stack<int>stk;
struct abcd{
int l,r,w;
abcd(){}
abcd(int a,int b,int c):l(a),r(b),w(c){}
};
vector<abcd>hehe1[MAXN+10],hehe2[MAXN+10];
void init(){
while(!stk.empty())stk.pop();
stk.push(1),l[1]=0;
for(int i=2;i<=n;++i){
while(!stk.empty()&&A[stk.top()]<A[i])r[stk.top()]=i,stk.pop();
if(stk.empty())l[i]=0;
else l[i]=stk.top();
stk.push(i);
}
while(!stk.empty())r[stk.top()]=n+1,stk.pop();
for(int i=1;i<=n;++i)hehe1[i].clear(),hehe2[i].clear();
for(int i=1;i<=n;++i){
//printf("[%d] l:%d r:%d\n",i,l[i],r[i]);
if(l[i]>0&&r[i]<=n)hehe1[l[i]].push_back(abcd(r[i],r[i],p1));
if(l[i]>0&&i<r[i]-1)hehe1[l[i]].push_back(abcd(i+1,r[i]-1,p2));
if(r[i]<=n&&i>l[i]+1)hehe2[r[i]].push_back(abcd(l[i]+1,i-1,p2));
}
}
int main(){
Read(n),Read(m),Read(p1),Read(p2);
for(int i=1;i<=n;++i)Read(A[i]);
init();
memset(root1,0,sizeof(root1));
memset(root2,0,sizeof(root2));
for(int i=1;i<=n;++i){
root1[i]=root1[i-1];
for(int j=0;j<hehe1[i].size();++j){
insert(1,n,hehe1[i][j].l,hehe1[i][j].r,hehe1[i][j].w,root1[i],root1[i]);
//printf("Line1 : %d (%d->%d) w:%d\n",i,hehe1[i][j].l,hehe1[i][j].r,hehe1[i][j].w);
}
if(i<n)insert(1,n,i+1,i+1,p1,root1[i],root1[i]);
}
for(int i=1;i<=n;++i){
root2[i]=root2[i-1];
for(int j=0;j<hehe2[i].size();++j){
insert(1,n,hehe2[i][j].l,hehe2[i][j].r,hehe2[i][j].w,root2[i],root2[i]);
//printf("Line2 : %d (%d->%d) w:%d\n",i,hehe2[i][j].l,hehe2[i][j].r,hehe2[i][j].w);
}
}
int a,b;
LL ans;
for(int i=1;i<=m;++i){
Read(a),Read(b);
ans=0;
ans+=query(1,n,a,b,root1[b]);
ans-=query(1,n,a,b,root1[a-1]);//printf("%I64d\n",ans);
ans+=query(1,n,a,b,root2[b]);
ans-=query(1,n,a,b,root2[a-1]);
printf("%lld\n",ans);
}
}