[HNOI2017]影魔

题目背景

影魔,奈文摩尔,据说有着一个诗人的灵魂。 事实上,他吞噬的诗人灵魂早已成千上万。

千百年来,他收集了各式各样的灵魂,包括诗人、 牧师、 帝王、 乞丐、 奴隶、 罪人,当然,还有英雄。

题目描述

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

奈文摩尔有 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 个询问。

输入输出样例

输入样例#1: 复制
10 5 2 3
7 9 5 1 3 10 6 8 2 4
1 7
1 9
1 3
5 9
1 5
输出样例#1: 复制
30
39
4
13
16

说明

30%: 1<= n,m <= 500。

另 30%: p1=2*p2。

100%:1 <= n,m <= 200000; 1 <= p1,p2 <= 1000。

solution:首先由于多组区间查询我们考虑离线算法,所以将查询按照左端点排序。对于攻击力p1满足max{k[s]|i<s<j}<=min{k[i],k[j]},而攻击力p2需满足min{k[i],k[j]}<=max{k[s]|i<s<j}<=max{k[i],k[j]};我们可以找到这两个条件的联系,设攻击p3为max{k[s]|i<s<j}<=max{k[i],k[j]},可知情况p3包含1、2。所以我们考虑如何求解情况3。设rt[i]为i右边第一个比k[i]大的值的下标(用单调栈得到此数组),所以如果以i为左端点可知以区间[i+1,rt[i]]会对答案产生贡献,那怎么区分是贡献p1还是p2呢,我们将区间[i+1,rt[i]]都加上p2,也就是将p1攻击也算成p2,由于对于以i为左端点的数对只有[i,rt[i]]会产生p1的贡献,且这个贡献将被计算两次(反向扫描也会多算一次),所以我们选择在rt[i]上加上p1-2×p2。接下来再将数组和询问翻转,再扫描一遍统计答案即可。

#include<bits/stdc++.h>
#define ll long long
#define N 200010
using namespace std;
int n,m,p1,p2,top,k[N],rt[N],stck[N];
ll ans[N];
struct question{
    int l,r,id;
}q[N];
struct node{
    ll sum,lz;
}sgm[N*10];
bool cmp(const question&a,const question&b)
{
    return a.l<b.l;
}
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f*=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void lazy(int rt,int l,int r,int mid)
{
    if(sgm[rt].lz)
    {
    ll  res=sgm[rt].lz;
    sgm[rt<<1].sum+=1ll*(mid-l+1)*res;
    sgm[rt<<1].lz+=res;
    sgm[rt<<1|1].sum+=1ll*(r-mid)*res;
    sgm[rt<<1|1].lz+=res;
    sgm[rt].lz=0;
    }
}
void build(int rt,int l,int r)
{
    sgm[rt].lz=0;
    if(l==r){sgm[rt].sum=0;return ;}
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    sgm[rt].sum=sgm[rt<<1].sum+sgm[rt<<1|1].sum;
}
void update(int rt,int l,int r,int L,int R,int val)
{
    if(L<=l&&R>=r){sgm[rt].sum+=1ll*(r-l+1)*val,sgm[rt].lz+=val;return;}
    int mid=(l+r)>>1;
    lazy(rt,l,r,mid);
    if(L<=mid)update(rt<<1,l,mid,L,R,val);
    if(R>mid)update(rt<<1|1,mid+1,r,L,R,val);
    sgm[rt].sum=sgm[rt<<1].sum+sgm[rt<<1|1].sum;
}
ll query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)return sgm[rt].sum;
    int mid=(l+r)>>1;
    lazy(rt,l,r,mid);
    ll ans=0;
    if(L<=mid)ans+=query(rt<<1,l,mid,L,R);
    if(R>mid)ans+=query(rt<<1|1,mid+1,r,L,R);
    return ans;
}
void work()
{
    memset(sgm,0,sizeof(sgm)),memset(stck,0,sizeof(stck));
    build(1,1,n+1);sort(q+1,q+1+m,cmp);k[stck[top=0]=n+1]=n+1;//将询问离线排序
    for(int i=n;i>=1;i--)//用单调栈找到i右边第一个比i大的数
    {
    while(top&&k[stck[top]]<k[i])top--;
    rt[i]=stck[top],stck[++top]=i;
    }
    for(int i=n,cur=m;i>=1;i--)//从右到左枚举左端点
    {
    update(1,1,n+1,i+1,rt[i],p2),update(1,1,n+1,rt[i],rt[i],p1-2*p2);
    while(cur&&q[cur].l==i)ans[q[cur].id]+=query(1,1,n+1,1,q[cur].r),cur--;//加上[l,r]的贡献
    }
}
int main()
{
    n=read(),m=read(),p1=read(),p2=read();
    for(int i=1;i<=n;i++)k[i]=read();
    for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].id=i;
    work();//正向扫描
    reverse(k+1,k+1+n);
    for(int i=1;i<=m;i++)q[i].l=n-q[i].l+1,q[i].r=n-q[i].r+1,swap(q[i].l,q[i].r);//调整询问
    work();//反向扫描
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

 

转载于:https://www.cnblogs.com/lxykk/p/8530794.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的描述,题目中的拥有n个灵魂,每个灵魂有一个战斗力ki。对于任意一对灵魂对i,j (i<j),如果不存在ks (i<s<j)大于ki或者kj,则会为提供p1的攻击力。另一种情况是,如果存在一个位置k,满足ki<c<kj或者kj<c<ki,则会为提供p2的攻击力。其他情况下的灵魂对不会为提供攻击力。 根据引用\[3\]的描述,我们可以从左到右进行枚举。对于情况1,当扫到r\[i\]时,更新l\[i\]的贡献。对于情况2.1,当扫到l\[i\]时,更新区间\[i+1,r\[i\]-1\]的贡献。对于情况2.2,当扫到r\[i\]时,更新区间\[l\[i\]+1,i-1\]的贡献。 因此,对于给定的区间\[l,r\],我们可以根据上述方法计算出区间内所有下标二元组i,j (l<=i<j<=r)的贡献之和。 #### 引用[.reference_title] - *1* *3* [P3722 [AH2017/HNOI2017](树状数组)](https://blog.csdn.net/li_wen_zhuo/article/details/115446022)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [洛谷3722 AH2017/HNOI2017 线段树 单调栈](https://blog.csdn.net/forever_shi/article/details/119649910)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值