bzoj 4826: [Hnoi2017]影魔

Description

影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。千百年来,他收集了各式各样
的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。每一个灵魂,都有着自己的战斗力,而影魔,靠
这些战斗力提升自己的攻击。奈文摩尔有 n 个灵魂,他们在影魔宽广的体内可以排成一排,从左至右标号 1 到 n。
第 i个灵魂的战斗力为 k[i],灵魂们以点对的形式为影魔提供攻击力,对于灵魂对 i,j(i<j)来说,若不存在 k[s](i
<s<j)大于 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 的排列:k1,k[2],...,k[n]。

solution

这题很久以前写过线段树做法,见这里
但线段树做法感觉已经超越人类智慧,不指望考场能够想得出,考虑主席树做法.
我在上一篇博客说过一句打酱油的话,但是这题主席树做法的关键:
当考虑一个位置 \(i\) 为最大值时,只有可能这两种情况产生贡献:
1.每组\([i,L[i]] [R[i],i] [i,i+1]\)可以贡献\(p1\)
2.\([L[i],j] (i+1<=j<=R[i]-1)\) \([j,R[i]](L[i]+1<=j<=i-1)\)都可以贡献\(p2\)
考虑处理询问,我们以左端点为下标,右段点为值域开主席树
注意一下标记可持久化

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=200041,M=12000005;
int n,m,a[N],st[N],L[N],R[N],tot=0;ll p1,p2;
struct node{
  int x,l,r,op;
  node(){}
  node(int _x,int _l,int _r,int _op){x=_x;l=_l;r=_r;op=_op;}
  bool operator <(const node &pr)const{return x<pr.x;}
}q[N<<2];
void priwork(){
  int top=0;
  for(int i=1;i<=n;i++){
    while(top && a[i]>a[st[top]])R[st[top]]=i,top--;
    L[i]=st[top];st[++top]=i;
  }
  while(top)R[st[top--]]=n+1;
}
struct Tree{
  int ls,rs,mark;ll val;
}tr[M];
int root[N],totnode=0;
inline void upd(int &x,int y,int l,int r,int sa,int se,ll t){
  x=++totnode;tr[x]=tr[y];
  if(l==sa && r==se){tr[x].val+=(r-l+1)*t;tr[x].mark+=t;return ;}
  int mid=(l+r)>>1;
  if(se<=mid)upd(tr[x].ls,tr[y].ls,l,mid,sa,se,t);
  else if(sa>mid)upd(tr[x].rs,tr[y].rs,mid+1,r,sa,se,t);
  else {
    upd(tr[x].ls,tr[y].ls,l,mid,sa,mid,t);
    upd(tr[x].rs,tr[y].rs,mid+1,r,mid+1,se,t);
  }
  tr[x].val=tr[tr[x].ls].val+tr[tr[x].rs].val+tr[x].mark*(r-l+1);
}
inline ll qry(int x,int y,int l,int r,int sa,int se,ll mr){
  if(l==sa && r==se)return tr[y].val-tr[x].val+(r-l+1)*mr;
  int mid=(l+r)>>1;
  ll ret=0;mr+=tr[y].mark-tr[x].mark;
  if(se<=mid)ret=qry(tr[x].ls,tr[y].ls,l,mid,sa,se,mr);
  else if(sa>mid)ret=qry(tr[x].rs,tr[y].rs,mid+1,r,sa,se,mr);
  else {
    ret+=qry(tr[x].ls,tr[y].ls,l,mid,sa,mid,mr);
    ret+=qry(tr[x].rs,tr[y].rs,mid+1,r,mid+1,se,mr);
  }
  return ret;
}
void work()
{
  scanf("%d%d%lld%lld",&n,&m,&p1,&p2);
  for(int i=1;i<=n;i++)scanf("%d",&a[i]);
  priwork();
  for(int i=1;i<=n;i++){
    if(L[i] && R[i]<=n)q[++tot]=node(L[i],R[i],R[i],1);
    if(R[i]<=n && L[i]+1<=i-1)q[++tot]=node(R[i],L[i]+1,i-1,2);
    if(L[i]>=1 && i+1<=R[i]-1)q[++tot]=node(L[i],i+1,R[i]-1,2);
  }
  int x=0,y;
  sort(q+1,q+tot+1);
  for(int i=1;i<=tot;i++){
    while(x<q[i].x)x++,root[x]=root[x-1];
    upd(root[x],root[x],1,n,q[i].l,q[i].r,(q[i].op==1?p1:p2));
  }
  for(int i=x;i<n;i++)root[i+1]=root[i];
  while(m--){
    scanf("%d%d",&x,&y);
    printf("%lld\n",qry(root[x-1],root[y],1,n,x,y,0)+(y-x)*p1);
  }
}

int main()
{
  work();
  return 0;
}

posted @ 2017-12-02 19:59  PIPIBoss  阅读(155)  评论(0编辑  收藏  举报