题目描述:
给出一个
1
1
1到
n
n
n的排列,一个数对
i
,
j
(
i
<
j
)
i,j(i<j)
i,j(i<j)的贡献定义为:
令
x
=
max
k
=
i
+
1
j
−
1
a
k
x=\max_{k=i+1}^{j-1}{a_k}
x=maxk=i+1j−1ak(若
i
=
=
j
−
1
i==j-1
i==j−1则
x
=
0
x=0
x=0)
若
x
<
min
(
a
i
,
a
j
)
x<\min(a_i,a_j)
x<min(ai,aj),则有
p
1
p_1
p1的贡献。
若
a
i
<
x
<
a
j
a_i<x<a_j
ai<x<aj或
a
i
>
x
>
a
j
a_i>x>a_j
ai>x>aj,则有
p
2
p_2
p2的贡献。
否则(
x
>
max
(
a
i
,
a
j
)
x>\max(a_i,a_j)
x>max(ai,aj)),没有贡献。
给出
m
m
m个询问,询问区间
[
l
,
r
]
[l,r]
[l,r]中所有数对的贡献之和。
题目分析:
可以考虑按照连续上升序列的做法统计贡献,但是还需要在单调队列上进行区间操作,比较麻烦。
在洛谷的第一篇题解看到一个非常棒(似乎也比较套路)的想法:按照数对之间的最大值统计贡献。
枚举每个位置
i
i
i作为最大值(没有最大值即i+1==j的情况可以直接计算),找到左边第一个比他大的位置
L
[
i
]
L[i]
L[i],右边第一个比他大的位置
R
[
i
]
R[i]
R[i]。
那么对于数对
(
L
[
i
]
,
R
[
i
]
)
(L[i],R[i])
(L[i],R[i])有
p
1
p_1
p1的贡献。
对于左端点在
[
L
[
i
]
+
1
,
i
−
1
]
[L[i]+1,i-1]
[L[i]+1,i−1],右端点为
R
[
i
]
R[i]
R[i]的数对有
p
2
p_2
p2的贡献。
同理,对于左端点为
L
[
i
]
L[i]
L[i],右端点在
[
i
+
1
,
R
[
i
]
−
1
]
[i+1,R[i]-1]
[i+1,R[i]−1]的数对有
p
2
p_2
p2的贡献。
容易想到从左往右扫然后区间修改,但是这里既有左端点又有右端点可能想法有些混乱。
对于前两种贡献我们在扫到
R
[
i
]
R[i]
R[i]的时候进行单点或区间修改,最后查询
[
l
,
r
]
[l,r]
[l,r]的和,很好做。
对于第三种贡献我们必须在扫到
L
[
i
]
L[i]
L[i]的时候就进行区间修改,但这样可能会导致
L
[
i
]
<
l
L[i]<l
L[i]<l却对区间
[
l
,
r
]
[l,r]
[l,r]做了贡献。这样的情况我们只需要在扫到
l
−
1
l-1
l−1的时候减去区间
[
l
,
r
]
[l,r]
[l,r]的答案,在扫到
r
r
r的时候加上区间
[
l
,
r
]
[l,r]
[l,r]的答案就可以了,这并不影响前两种贡献,所以可以用一个树状数组完成。
对左端点的区间修改进行差分这个技巧很妙,枚举最大值做贡献也很妙,但是比较常见了。
Code:
#include<bits/stdc++.h>
#define maxn 200005
#define LL long long
using namespace std;
int n,m,cnt,a[maxn],p1,p2,L[maxn],R[maxn],pos[maxn];
LL ans[maxn],a1[maxn],a2[maxn];
struct node{
int l,r,x,id;
bool operator < (const node &p)const{return x<p.x;}
}q[maxn<<1],t[maxn*3];
void upd(int x,int d){for(int i=x;i<=n;i+=i&-i) a1[i]+=d,a2[i]+=x*d;}
LL qsum(int x){LL s1=0,s2=0;for(int i=x;i;i-=i&-i) s1+=a1[i],s2+=a2[i];return (x+1)*s1-s2;}
int main()
{
int x,y;
scanf("%d%d%d%d",&n,&m,&p1,&p2);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),L[i]=i-1,R[i]=i+1,pos[a[i]]=i;
for(int i=1;i<=n;i++) x=pos[i],R[L[x]]=R[x],L[R[x]]=L[x];
for(int i=1;i<=n;i++){
if(1<=L[i]&&R[i]<=n) t[++cnt]=(node){L[i],L[i],R[i],p1};
if(L[i]<i-1&&R[i]<=n) t[++cnt]=(node){L[i]+1,i-1,R[i],p2};
if(L[i]&&i+1<R[i]) t[++cnt]=(node){i+1,R[i]-1,L[i],p2};
}
for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),ans[i]=(y-x)*p1,q[i]=(node){x,y,x-1,i},q[i+m]=(node){x,y,y,i};
sort(q+1,q+1+2*m),sort(t+1,t+1+cnt);
for(int i=1,j=1;i<=2*m;i++){
while(j<=cnt&&t[j].x<=q[i].x) upd(t[j].l,t[j].id),upd(t[j].r+1,-t[j].id),j++;
ans[q[i].id]+=(q[i].x<q[i].l?-1:1)*(qsum(q[i].r)-qsum(q[i].l-1));
}
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}