题目背景
影魔,奈文摩尔,据说有着一个诗人的灵魂。 事实上,他吞噬的诗人灵魂早已成千上万。
千百年来,他收集了各式各样的灵魂,包括诗人、 牧师、 帝王、 乞丐、 奴隶、 罪人,当然,还有英雄。
题目描述
每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。
奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i, j(i<j)来说,若不存在 ks大于 k[i]或者 k[j],则会为影魔提供 p1 的攻击力(可理解为: 当 j=i+1 时,因为不存在满足 i<s<j 的 s,从而 k[s]不存在,这时提供 p1 的攻击力;当 j>i+1 时,若max{k[s]|i<s<j}<=min{k[i],k[j]} , 则 提 供 p1 的 攻 击 力 ); 另 一 种 情 况 , 令 c 为k[i+1],k[i+2],k[i+3]……k[j-1]的最大值,若 c 满足: k[i]<c<k[j],或者 k[j]<c<k[i],则会为影魔提供 p2 的攻击力,当这样的 c 不存在时,自然不会提供这 p2 的攻击力;其他情况的点对,均不会为影魔提供攻击力。
影魔的挚友噬魂鬼在一天造访影魔体内时被这些灵魂吸引住了,他想知道,对于任意一段区间[a,b], 1<=a<b<=n,位于这些区间中的灵魂对会为影魔提供多少攻击力,即考虑 所有满足a<=i<j<=b 的灵魂对 i,j 提供的攻击力之和。
顺带一提,灵魂的战斗力组成一个 1 到 n 的排列: k[1],k[2],…,k[n]。
输入输出格式
输入格式:输入文件名为 sf.in。
第一行 n,m,p1,p2
第二行 n 个数: k[1],k[2],…,k[n]
接下来 m 行, 每行两个数 a,b, 表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。
输出文件名为 sf.out
共输出 m 行,每行一个答案,依次对应 m 个询问。
输入输出样例
10 5 2 3 7 9 5 1 3 10 6 8 2 4 1 7 1 9 1 3 5 9 1 5
30 39 4 13 16
说明
30%: 1<= n,m <= 500。
另 30%: p1=2*p2。
100%:1 <= n,m <= 200000; 1 <= p1,p2 <= 1000。
线段树+离线预处理+扫描线算法。
个人认为AHOI2017 DAY1 最难一题。。。
对于i,找到右边大于他的第一个数,如果没有,自然是最后一个数,但是这样不好,于是我们加一个第 n+1 个数为+∞。
然后设 ri 为右边大于他的第一个数,那么 [ i , i+1 ]、[ i , i+2 ]、...... [ i , ri ] 都符合条件,于是在线段树中对于 [ i+1 , ri ] 都 += p2。
对于全部询问排序,给以i为左端点的区间加上贡献,查询[i+1,R]的和。反过来再搞一遍,注意询问区间也要反过来。
然后我们发现题目中的对另一个端点的限制是互补的,我们的区间 [ i , ri ] 满足 p1,而 [ i , i+1 ~ ri-1 ] 满足 p2。
因为只有一个满足p1,而且一个i对应一个ri,于是我们在ri处 + p1 - p2 * 2 即可,完美解决这一个问题。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define LSON rt<<1
#define RSON rt<<1|1
#define DATA(x) a[x].data
#define SIGN(x) a[x].c
#define LSIDE(x) a[x].l
#define RSIDE(x) a[x].r
#define WIDTH(x) (RSIDE(x)-LSIDE(x)+1)
#define MAXN 200010
using namespace std;
int n,m,top;
int next[MAXN],stack[MAXN];
long long p1,p2,val[MAXN],ans[MAXN];
struct Sg_Tree{
long long data,c;
int l,r;
}a[MAXN<<2];
struct node{
int l,r,id;
}que[MAXN];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
bool cmp(const node &x,const node &y){
if(x.l==y.l)return x.r<y.r;
return x.l<y.l;
}
inline void pushup(int rt){
DATA(rt)=DATA(LSON)+DATA(RSON);
}
inline void pushdown(int rt){
if(!SIGN(rt)||LSIDE(rt)==RSIDE(rt))return;
SIGN(LSON)+=SIGN(rt);
DATA(LSON)+=SIGN(rt)*WIDTH(LSON);
SIGN(RSON)+=SIGN(rt);
DATA(RSON)+=SIGN(rt)*WIDTH(RSON);
SIGN(rt)=0;
}
void buildtree(int l,int r,int rt){
int mid;
LSIDE(rt)=l;
RSIDE(rt)=r;
SIGN(rt)=0;
if(l==r){
DATA(rt)=0;
return;
}
mid=l+r>>1;
buildtree(l,mid,LSON);
buildtree(mid+1,r,RSON);
pushup(rt);
}
void update(int l,int r,long long c,int rt){
int mid;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r){
SIGN(rt)+=c;
DATA(rt)+=c*WIDTH(rt);
return;
}
pushdown(rt);
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)update(l,r,c,LSON);
if(mid<r)update(l,r,c,RSON);
pushup(rt);
}
long long query(int l,int r,int rt){
int mid;
long long ans=0;
if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return DATA(rt);
pushdown(rt);
mid=LSIDE(rt)+RSIDE(rt)>>1;
if(l<=mid)ans+=query(l,r,LSON);
if(mid<r)ans+=query(l,r,RSON);
return ans;
}
int half_find(int x){
int left=1,right=m+1,mid;
while(left<=right){
mid=left+right>>1;
if(que[mid].l<x)left=mid+1;
else right=mid-1;
}
return left;
}
void work(){
top=0;
stack[top]=n+1;
buildtree(1,n+1,1);
for(int i=n;i>=1;i--){
while(top&&val[stack[top]]<val[i])stack[top--]=0;
next[i]=stack[top];
stack[++top]=i;
}
for(int i=n;i>=1;i--){
if(next[i]>i){
update(i+1,next[i],p2,1);
update(next[i],next[i],p1-p2*2,1);
}
int pos=half_find(i);
while(que[pos].l==i){
ans[que[pos].id]+=query(i+1,que[pos].r,1);
pos++;
}
}
}
void init(){
n=read();m=read();p1=read();p2=read();
for(int i=1;i<=n;i++)val[i]=read();
for(int i=1;i<=m;i++){
que[i].l=read();que[i].r=read();
que[i].id=i;
}
sort(que+1,que+m+1,cmp);
work();
for(int i=1;i<=n/2;i++)swap(val[i],val[n-i+1]);
for(int i=1;i<=m;i++){
swap(que[i].l,que[i].r);
que[i].l=n-que[i].l+1;
que[i].r=n-que[i].r+1;
}
sort(que+1,que+m+1,cmp);
work();
for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
}
int main(){
init();
work();
return 0;
}