這個題的思路還是很巧妙的。
sro hjq orz
sro Creed orz
首先,我們來考慮題目中給出的兩個條件,由於都是跟最大值有關係,所以我們可能會想到,首先運用單調棧求出來每一個數左邊第一個比他大的數的位置和右邊第一個比他大的數的位置
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]++;
考慮將詢問離散,依次枚舉每個點,然後更新他對於其他點的貢獻,如果我們能快速求出來一個點與其他點的貢獻的話,那我們就可以對於一個詢問,在處,求出來的貢獻(其他點與這個點的貢獻)和,然後在處再求一遍,兩個做減法,就是中間的總貢獻,其實也就是題目求的東西了。
那麼問題就是,我們該如何維護並求出這個點與別的點的貢獻。
我們會發現,對於題目中的第一個條件,實際上就是在處對於有貢獻。
而第二個條件就是在處對於有有p2的貢獻,而在處,對於有貢獻。
那麼就是一個區間加,區間求和,可以選擇線段樹來維護。
然後對於每一次修改或者是詢問,存到一個裏面,離線排序來處理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;
}