【BZOJ4826】【HNOI2017】影魔(扫描线,单调栈)

232 篇文章 0 订阅
10 篇文章 0 订阅

题面

BZOJ
洛谷

Description

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

Input

第一行 n,m,p1,p2
第二行 n 个数:k[1],k[2],…,k[n]
接下来 m 行,每行两个数 a,b,表示询问区间[a,b]中的灵魂对会为影魔提供多少攻击力。
1 <= n,m <= 200000;1 <= p1,p2 <= 1000

Output

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

Sample Input

10 5 2 3

7 9 5 1 3 10 6 8 2 4

1 7

1 9

1 3

5 9

1 5

Sample Output

30

39

4

13

16

题解

这题和序列很类似
但是我依然不是自己做出来的
还是太弱了


首先把两种贡献换成人话:
如果对于两个位置 l,r
他们是区间 [l,r] 中的最大值和次大值,产生 p1 的贡献
如果恰好有一个是最大值,产生 p2 的贡献

那么,对于当前位置 i ,假设左/右第一个比他大的位置是l,r
那么, (l,r) 产生 p1 的贡献
(l+1..i1,r),(l,i+1..r1) 会产生 p2 的贡献
可以把他分解为二维平面内的矩阵求和问题,这个可以用扫描线来解决

当然,主要的问题是怎么转换为二维平面求矩阵和。。。

我们算的是当前加上了位置 i 以后产生的贡献
如果对于一个询问L,R i 在其范围内,当然就要考虑它产生的贡献
那么,i产生的贡献是什么?
就是我们前面考虑过的问题
将一个位置 i 拆分成3个贡献:
当加入完位置 R[i] 之后,要对于 L[i] 产生 p1 的贡献
当加入完位置 L[i] 之后,要对于 (i,R[i]) 的每个位置产生 p2 的贡献
当加入完位置 R[i] 之后,要对于 (L[i],i) 的每个位置产生 p2 的贡献

那么,一个询问 L,R ,只要考虑它范围内的贡献
因此拆分成三部分
加入完 L1 位置之后,要忽略掉前面所有 L,R 位置产生的贡献
加入完 R 位置之后,要考虑所有的L,R位置产生的贡献
额外考虑每一组范围内的, (i,i+1) 产生的贡献 p1

这样子,扫描线+区间加法+区间求和即可
可以用单调栈+树状数组实现。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 222222
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
int L[MAX],R[MAX];
int a[MAX],n,m,p1,p2;
int S[MAX],top,cnt,tot;
struct Line{int x,l,r,v;}p[MAX<<2];
bool operator<(Line a,Line b){return a.x<b.x;}
struct query{int x,l,r,v,id;}q[MAX<<2];
bool operator<(query a,query b){return a.x<b.x;}
ll c1[MAX],c2[MAX],ans[MAX];
void Modify(int x,int w)
{
    for(int i=x;i<=n;i+=i&(-i))
        c1[i]+=w,c2[i]+=x*w;
}
ll Query(int x)
{
    ll ret=0;
    for(int i=x;i;i-=i&(-i))
        ret+=(x+1)*c1[i]-c2[i];
    return ret;
}
int main()
{
    n=read();m=read();p1=read();p2=read();
    for(int i=1;i<=n;++i)a[i]=read();
    S[top=0]=0;
    for(int i=1;i<=n;++i)
    {
        while(top&&a[S[top]]<a[i])--top;
        L[i]=S[top];S[++top]=i;
    }
    S[top=0]=n+1;
    for(int i=n;i>=1;--i)
    {
        while(top&&a[S[top]]<a[i])--top;
        R[i]=S[top];S[++top]=i;
    }
    for(int i=1;i<=m;++i)
    {
        int l=read(),r=read();ans[i]=(r-l)*p1;
        q[++cnt]=(query){r,l,r,1,i};
        q[++cnt]=(query){l-1,l,r,-1,i};
    }
    sort(&q[1],&q[cnt+1]);
    for(int i=1;i<=n;++i)
    {
        if(L[i]&&R[i]<n+1)p[++tot]=(Line){R[i],L[i],L[i],p1};
        if(L[i]&&R[i]>i+1)p[++tot]=(Line){L[i],i+1,R[i]-1,p2};
        if(L[i]+1<i&&R[i]<n+1)p[++tot]=(Line){R[i],L[i]+1,i-1,p2};
    }
    sort(&p[1],&p[tot+1]);
    for(int i=1,j=1;i<=cnt;++i)
    {
        while(j<=tot&&p[j].x<=q[i].x)
        {
            Modify(p[j].l,p[j].v);
            Modify(p[j].r+1,-p[j].v);
            ++j;
        }
        ans[q[i].id]+=q[i].v*(Query(q[i].r)-Query(q[i].l-1));
    }
    for(int i=1;i<=m;++i)printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值