P3722 [AH2017/HNOI2017]影魔 (單調棧+線段樹+離線)

這個題的思路還是很巧妙的。
sro hjq orz
sro Creed orz

首先,我們來考慮題目中給出的兩個條件,由於都是跟最大值有關係,所以我們可能會想到,首先運用單調棧求出來每一個數左邊第一個比他大的數的位置和右邊第一個比他大的數的位置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]++;

考慮將詢問離散,依次枚舉每個點,然後更新他對於其他點的貢獻,如果我們能快速求出來一個點與其他點的貢獻的話,那我們就可以對於一個詢問,在l1l-1處,求出來[l,r][l,r]的貢獻(其他點與這個點的貢獻)和,然後在rr處再求一遍,兩個做減法,就是中間的總貢獻,其實也就是題目求的東西了。

那麼問題就是,我們該如何維護並求出這個點與別的點的貢獻。

我們會發現,對於題目中的第一個條件,實際上就是在r[i]r[i]處對於l[i]l[i]有貢獻。
而第二個條件就是在l[i]l[i]處對於i+1r[i]1i+1到r[i]-1有有p2的貢獻,而在r[i]r[i]處,對於l[i]+1,i1l[i]+1,i-1有貢獻。

那麼就是一個區間加,區間求和,可以選擇線段樹來維護。
然後對於每一次修改或者是詢問,存到一個structstruct裏面,離線排序來處理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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章