【Hnoi2017】影魔

题目描述

影魔,奈文摩尔,据说有着一个诗人的灵魂。事实上,他吞噬的诗人灵魂早已成千上万。千百年来,他收集了各式各样的灵魂,包括诗人、牧师、帝王、乞丐、奴隶、罪人,当然,还有英雄。

每一个灵魂,都有着自己的战斗力,而影魔,靠这些战斗力提升自己的攻击。

奈文摩尔有\(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\)的排列:\(k[1],k[2],…,k[n]\)

输入

第一行\(n,m,p1,p2\)

第二行\(n\)个数:\(k[1],k[2],…,k[n]\)

接下来\(m\)行,每行两个数\(a,b\),表示询问区间\([a,b]\)中的灵魂对会为影魔提供多少攻击力。

输出

共输出\(m\)行,每行一个答案,依次对应\(m\)个询问。


我们先考虑对于\(p2\)的处理

我们先选择性的舍弃条件,我们发现,我们可以对于一个两个端点中的任意一个大于整个区间所有数的区间\(+p2\)

那么,这里可以用线段树来解决

我们先用区间覆盖+单点最大线段树求出位置\(x\)的右边第一个比\(x\)大的数的位置\(g[i]\)和位置\(x\)的左边第一个比\(x\)大的数的位置\(f[i]\)

然后正着反着各扫一遍,以反着扫为例

对询问按照左端点升序排序,然后对于当前位置\(x\),对区间\([x+1,g(x)]\)进行\(+p2\)的操作,而对于当前扫到的询问左端点\(x\),显然,他对答案的贡献是\(\sum [1,y]\)\(y\)是询问的右端点)

正着扫类似

但是这样做的话,我们发现\(+p2\)的答案会偏大,多处的一部分正是\(+p1\)所要的,于是\(p1=p2*2\)部分就解决了

那么,怎么把那一部分从\(+p2\)改成\(+p1\)呢?

我们对于一个区间只需要对位置\(x\),对于\(g(x)\)单点\(-p2*2+p1\)即可,挺容易想到

但是要注意,因为当左边或者右边没有数比他大的时候,我们会把\(g(x)=n\)\(f(x)=1\),而这样的区间是不符合\(+p1\)的,不要执行单点加减的操作

#include<cstdio>  
#include<iostream>  
#include<algorithm>  
#include<cstdlib>  
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s)
{
    char c=nc();int len=0;
    for(;!(c>='A' && c<='Z');c=nc()) if (c==EOF) return 0;
    for(;(c>='A' && c<='Z');s[len++]=c,c=nc());
    s[len++]='\0';
    return len;
}

inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,a[200010],f[200010],g[200010];
LL p1,p2,ans[200010];
struct st
{
    LL sum,tag;
}tree[800010];
struct st2
{
    int mi,tag;
}b[800010];
struct data
{
    int x,y,id;
}q[200010];

void Pushdown(int x,int l,int r)
{
    if (tree[x].tag!=0)
    {
        tree[x<<1].tag+=tree[x].tag;tree[x<<1|1].tag+=tree[x].tag;
        tree[x].sum+=tree[x].tag*(LL)(r-l+1);
        tree[x].tag=0;
    }
}

void Pushup(int x,int l,int mid,int r)
{
    tree[x].sum=tree[x<<1].sum+tree[x<<1|1].sum+tree[x<<1].tag*(LL)(mid-l+1)+tree[x<<1|1].tag*(LL)(r-mid);
}

void change(int lq,int rq,int l,int r,int x,LL z)
{
    if (lq<=l && rq>=r) {tree[x].tag+=z;return ;}
    int mid=l+r>>1;
    Pushdown(x,l,r);
    if (lq<=mid) change(lq,rq,l,mid,x<<1,z);
    if (rq>mid) change(lq,rq,mid+1,r,x<<1|1,z);
    Pushup(x,l,mid,r);
}

void changemin(int lq,int rq,int l,int r,int x,int z)
{
    if (lq<=l && rq>=r) {b[x].tag=z;return ;}
    int mid=l+r>>1;
    if (b[x].tag<n)
    {
        b[x<<1].tag=b[x].tag;b[x<<1|1].tag=b[x].tag;
        b[x].mi=min(b[x].mi,b[x].tag);b[x].tag=n;
    }
    if (lq<=mid) changemin(lq,rq,l,mid,x<<1,z);
    if (rq>mid) changemin(lq,rq,mid+1,r,x<<1|1,z);
    b[x].mi=min(min(b[x<<1].mi,b[x<<1].tag),min(b[x<<1|1].mi,b[x<<1|1].tag));
}

LL querysum(int lq,int rq,int l,int r,int x)
{
    if (lq<=l && rq>=r) return tree[x].sum+tree[x].tag*(LL)(r-l+1);
    int mid=l+r>>1;
    LL res=0;
    Pushdown(x,l,r);
    if (lq<=mid) res+=querysum(lq,rq,l,mid,x<<1);
    if (rq>mid) res+=querysum(lq,rq,mid+1,r,x<<1|1);
    Pushup(x,l,mid,r);
    return res;
}

int querymin(int lq,int rq,int l,int r,int x)
{
    if (lq<=l && rq>=r) return min(b[x].mi,b[x].tag);
    int mid=l+r>>1,res=INT_MAX;
    if (b[x].tag<n)
    {
        b[x<<1].tag=b[x].tag;b[x<<1|1].tag=b[x].tag;
        b[x].mi=min(b[x].mi,b[x].tag);b[x].tag=n;
    }
    if (lq<=mid) res=min(res,querymin(lq,rq,l,mid,x<<1));
    if (rq>mid)res=min(res,querymin(lq,rq,mid+1,r,x<<1|1));
    b[x].mi=min(min(b[x<<1].mi,b[x<<1].tag),min(b[x<<1|1].mi,b[x<<1|1].tag));
    return res;
}

void build(int l,int r,int x)
{
    b[x].mi=n,b[x].tag=n;
    if (l==r) return ;
    int mid=l+r>>1;
    build(l,mid,x<<1);build(mid+1,r,x<<1|1);
}

bool cmp1(data x,data y)
{
    return x.x>y.x;
}

bool cmp2(data x,data y)
{
    return x.y<y.y;
}

int main()
{
    read(n);read(m);read(p1);read(p2);
    for (int i=1;i<=n;i++)
        read(a[i]);
    for (int i=1;i<=m;i++)
        read(q[i].x),read(q[i].y),q[i].id=i;
    build(1,n,1);
    for (int i=n;i>=1;i--)
    {
        if (a[i]==n) f[i]=n;
        else f[i]=querymin(a[i],n,1,n,1);
        if (a[i]!=1) changemin(1,a[i]-1,1,n,1,i);
    }
    for (int i=1;i<=n;i++)
        g[i]=f[i];
    build(1,n,1);
    for (int i=1;i<=n/2;i++)
        swap(a[i],a[n-i+1]);
    for (int i=n;i>=1;i--)
    {
        if (a[i]==n) f[i]=1;
        else f[i]=n-querymin(a[i],n,1,n,1)+1;
        if (a[i]!=1) changemin(1,a[i]-1,1,n,1,i);
    }
    for (int i=1;i<=n/2;i++)
        swap(f[i],f[n-i+1]);
    for (int i=1;i<=n/2;i++)
        swap(a[i],a[n-i+1]);
    sort(q+1,q+1+m,cmp1);
    memset(tree,0,sizeof(tree));
    memset(ans,0,sizeof(ans));
    int Q=1;
    for (int i=n;i>=1;i--)
    {
        if (i+1<=g[i])
        {
            change(i+1,g[i],1,n,1,p2);
            if (a[i]<a[g[i]]) change(g[i],g[i],1,n,1,-p2*2+p1);
        }
        while (q[Q].x==i)
        {
            ans[q[Q].id]+=querysum(1,q[Q].y,1,n,1);
            Q++;
        }
    }
    sort(q+1,q+1+m,cmp2);
    memset(tree,0,sizeof(tree));
    Q=1;
    for (int i=1;i<=n;i++)
    {
        if(f[i]<=i-1)
        {
            change(f[i],i-1,1,n,1,p2);
            if (a[f[i]]>a[i]) change(f[i],f[i],1,n,1,-p2*2+p1);
        }
        while (q[Q].y==i)
        {
            ans[q[Q].id]+=querysum(q[Q].x,n,1,n,1);
            Q++;
        }
    }
    for (int i=1;i<=m;i++)
        print(ans[i]),puts("");
    return 0;
}

转载于:https://www.cnblogs.com/xiejiadong/p/6772562.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值