P3722 [AH2017/HNOI2017]影魔 (单调栈+线段树+离线)

这个题的思路还是很巧妙的。
sro hjq orz
sro Creed orz

首先,我们来考虑题目中给出的两个条件,由于都是跟最大值有关系,所以我们可能会想到,首先运用单调栈求出来每一个数左边第一个比他大的数的位置和右边第一个比他大的数的位置 l [ i ] , r [ i ] l[i],r[i] l[i],r[i]

for (int i=1;i<=n;i++) a[i]=read();
    l[1]=1;
    top=1;
    s[1].val=a[1];
    s[1].pos=1; 
    for (int i=2;i<=n;i++)
    {
       while (top>=1 && s[top].val<a[i]) top--;
       if (!top) l[i]=1;
       else l[i]=s[top].pos+1;
       s[++top].pos=i;
       s[top].val=a[i];
    }
    r[n]=n;
    top=1;
    s[1].val=a[n];
    s[1].pos=n;
    for (int i=n-1;i>=1;i--)
    {
        while (top>=1 && s[top].val<a[i]) top--;
        if (!top) r[i]=n;
        else r[i]=s[top].pos-1;
        s[++top].pos=i;
        s[top].val=a[i];
    }
    for (int i=1;i<=n;i++) l[i]--,r[i]++;

考虑将询问离散,依次枚举每个点,然后更新他对于其他点的贡献,如果我们能快速求出来一个点与其他点的贡献的话,那我们就可以对于一个询问,在 l − 1 l-1 l1处,求出来 [ l , r ] [l,r] [l,r]的贡献(其他点与这个点的贡献)和,然后在 r r r处再求一遍,两个做减法,就是中间的总贡献,其实也就是题目求的东西了。

那么问题就是,我们该如何维护并求出这个点与别的点的贡献。

我们会发现,对于题目中的第一个条件,实际上就是在 r [ i ] r[i] r[i]处对于 l [ i ] l[i] l[i]有贡献。
而第二个条件就是在 l [ i ] l[i] l[i]处对于 i + 1 到 r [ i ] − 1 i+1到r[i]-1 i+1r[i]1有有p2的贡献,而在 r [ i ] r[i] r[i]处,对于 l [ i ] + 1 , i − 1 l[i]+1,i-1 l[i]+1,i1有贡献。

那么就是一个区间加,区间求和,可以选择线段树来维护。
然后对于每一次修改或者是询问,存到一个 s t r u c t struct struct里面,离线排序来处理2333.

qwq

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#define mk make_pair
#define pb push_back
#define ll long long
#define int long long
using namespace std;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}

const int maxn = 1e6+1e2;

int l[maxn],r[maxn],a[maxn];
int f[4*maxn];
int add[4*maxn];
int n,m;
int ans[maxn];

struct Node{
	int x,l,r,num,opt;
};

Node q[maxn];

struct st{
  int val,pos;
};

st s[maxn];
Node p[maxn];
int p1,p2;

void up(int root)
{
	f[root]=f[2*root]+f[2*root+1];
}

void pushdown(int root,int l,int r)
{
	if (add[root])
	{
		add[2*root]+=add[root];
		add[2*root+1]+=add[root];
        int mid = l+r >> 1;
		f[2*root]+=(mid-l+1)*add[root];
		f[2*root+1]+=(r-mid)*add[root];
		add[root]=0;
	}
}

void update(int root,int l,int r,int x,int y,int p)
{
      if (x>y || x==0 || y==0) return;
	if (x<=l && r<=y)
	{
		add[root]+=p;
		f[root]+=(r-l+1)*p;
		return;
	}
	int mid = l+r >> 1;
	pushdown(root,l,r);
	if (x<=mid) update(2*root,l,mid,x,y,p);
	if (y>mid) update(2*root+1,mid+1,r,x,y,p);
	up(root);
}

int query(int root,int l,int r,int x,int y)
{
      if (x>y || x==0 || y==0) return 0;
	if (x<=l && r<=y) return f[root];
	int mid = l+r >> 1;
	pushdown(root,l,r);
	int ans=0;
	if (x<=mid) ans=ans+query(2*root,l,mid,x,y);
	if (y>mid) ans=ans+query(2*root+1,mid+1,r,x,y);
	return ans;
}

int top;

bool cmp(Node a,Node b)
{
   return a.x<b.x;
};

signed main()
{
    n=read(),m=read(),p1=read(),p2=read();
    for (int i=1;i<=n;i++) a[i]=read();
    l[1]=1;
    top=1;
    s[1].val=a[1];
    s[1].pos=1; 
    for (int i=2;i<=n;i++)
    {
       while (top>=1 && s[top].val<a[i]) top--;
       if (!top) l[i]=1;
       else l[i]=s[top].pos+1;
       s[++top].pos=i;
       s[top].val=a[i];
    }
    r[n]=n;
    top=1;
    s[1].val=a[n];
    s[1].pos=n;
    for (int i=n-1;i>=1;i--)
    {
        while (top>=1 && s[top].val<a[i]) top--;
        if (!top) r[i]=n;
        else r[i]=s[top].pos-1;
        s[++top].pos=i;
        s[top].val=a[i];
    }
    for (int i=1;i<=n;i++) l[i]--,r[i]++;
    //for (int i=1;i<=n;i++) cout<<l[i]<<" "<<r[i]<<endl;
    int tot=0,cnt=0;
    for (int i=1;i<=m;i++) 
    {
       int x=read(),y=read();
       ans[i]+=(y-x)*p1;
       if (x!=1)p[++tot]=(Node){x-1,x,y,i,-1};
       p[++tot]=(Node){y,x,y,i,1};
    }
    sort(p+1,p+1+tot,cmp);
    for (int i=1;i<=n;i++)
    {
       if (r[i]<=n) q[++cnt]=(Node){r[i],l[i],l[i],i,p1};
       if (l[i]>0) q[++cnt]=(Node){l[i],i+1,r[i]-1,i,p2};
       if (r[i]<=n) q[++cnt]=(Node){r[i],l[i]+1,i-1,i,p2};
    }
    sort(q+1,q+1+cnt,cmp);
    int x=1,y=1;
    for (int i=1;i<=n;i++)
    {
       while (x<=cnt && q[x].x<=i) 
       {
          //cout<<i<<" "<<q[x].l<<" "<<q[x].r<<endl;
          update(1,1,n,q[x].l,q[x].r,q[x].opt);
          x++;
       }
       //cout<<"****"<<endl;
       while (y<=tot && p[y].x<=i)
       {
          ans[p[y].num]+=p[y].opt*query(1,1,n,p[y].l,p[y].r);
          y++;
       }
       
    } 
    for (int i=1;i<=m;i++) cout<<ans[i]<<"\n";
    return 0;
}
  • 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、付费专栏及课程。

余额充值