题解 P3722 【[AH/HNOI2017]影魔】

· · 题解

题解:

这个题可以采取离线处理的方式.先处理出每个点i左边第一个比它大的点L[i],和右边第一个比它大的点R[i].

那么对于区间L[i]到R[i]有p1的贡献.①

对于左端点在L[i]+1到i-1,右端点为R[i]的区间有p2的贡献.②

对于左端点为L[i],右端点为i+1到R[i]-1的区间也有p2的贡献.③

所以我们离线排序处理好.

对于①情况,我们在扫到R[i]时,更新点L[i]的贡献

对于②情况,我们在扫到R[i]时,更新区间L[i]+1到i-1的贡献

对于③情况,我们在扫到L[i]时,更新区间i+1到R[i]-1的贡献

我们对于每个询问[l,r],在扫到l-1时,我们记录此时区间l到r的每个点的贡献和为sum1,然后当我们扫到r的时候,再次记录此时的区间l到r的每个点的贡献和为sum2,显然答案就是sum2-sum1了.

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<set>
#define RG register
#define LL long long int
#define MAXN 500010
using namespace std;
const int INF=1e9;
struct node{
  int l,r,x,id,v;
  node(){}
  node(int l_,int r_,int x_,int id_,int v_):l(l_),r(r_),x(x_),id(id_),v(v_){}
  bool operator <(const node &tmp)const{
    return x<tmp.x;
  }
}s1[MAXN],s2[MAXN];
int n,m,p1,p2;
int k[MAXN],q[MAXN],top;
int L[MAXN],R[MAXN];
LL ans[MAXN],c1[MAXN],c2[MAXN];
int lowbit(int x)
{
  return x&(-x);
}
void add(int x,int y)//区间修改操作
{
  if(x) for(int i=x;i<=n;i+=lowbit(i)) c1[i]+=y,c2[i]+=(LL)x*y;
}
LL sum(int x)
{
  LL num=0;
  for(int i=x;i>0;i-=lowbit(i)) num+=(x+1)*c1[i]-c2[i];
  return num;
}
int main()
{
  freopen("1.in","r",stdin);
  scanf("%d%d%d%d",&n,&m,&p1,&p2);
  k[0]=k[n+1]=n+1;q[++top]=0;
  for(int i=1;i<=n;i++) scanf("%d",&k[i]);
  for(int i=1;i<=n+1;i++)
    {
      while(k[q[top]]<k[i]) R[q[top]]=i,top--;
      L[i]=q[top];q[++top]=i;
    }
  int x,y;
  for(int i=1;i<=m;i++)
    {
      scanf("%d%d",&x,&y);ans[i]+=(y-x)*p1;
      s1[i]=node(x,y,x-1,i,-1);s1[i+m]=node(x,y,y,i,1);
    }
  sort(s1+1,s1+2*m+1);int tot=0;
  for(int i=1;i<=n;i++)
    {
      if(1<=L[i]&&R[i]<=n) s2[++tot]=node(L[i],L[i],R[i],0,p1);
      if(1<=L[i]&&R[i]>i+1) s2[++tot]=node(i+1,R[i]-1,L[i],0,p2);
      if(L[i]+1<i&&R[i]<=n) s2[++tot]=node(L[i]+1,i-1,R[i],0,p2);
    }
  sort(s2+1,s2+tot+1);int n1=1,n2=1;
  while(!s1[n1].x) n1++;
  for(int i=1;n1<=m*2&&i<=n;i++)
    {
      while(n2<=tot&&s2[n2].x==i){
    add(s2[n2].r+1,-s2[n2].v);
    add(s2[n2].l,s2[n2].v);
    n2++;
      }
      while(n1<=m*2&&s1[n1].x==i){
    ans[s1[n1].id]+=s1[n1].v*(sum(s1[n1].r)-sum(s1[n1].l-1));
    n1++;
      } 
    }
  for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
  return 0;
}