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
#include
#include
#include
#include
#include
#include
#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<
    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<
          update(1,1,n,q[x].l,q[x].r,q[x].opt);
          x++;
       }
       //cout<<"****"<
       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;
}

你可能感兴趣的:(线段树,单调栈,c++)