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

博客介绍了一道题的巧妙解题思路。先运用单调栈求出每个数左右第一个比它大的数的位置,将询问离散,枚举点更新其对其他点的贡献。通过在特定位置求贡献和做减法得到题目所求。对于点与其他点的贡献维护,采用线段树处理区间加和区间求和,修改和询问离线排序处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个题的思路还是很巧妙的。
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−1l-1l1处,求出来[l,r][l,r][l,r]的贡献(其他点与这个点的贡献)和,然后在rrr处再求一遍,两个做减法,就是中间的总贡献,其实也就是题目求的东西了。

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

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

那么就是一个区间加,区间求和,可以选择线段树来维护。
然后对于每一次修改或者是询问,存到一个structstructstruct里面,离线排序来处理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);