【BZOJ4826】影魔(AHOI&HNOI2017)-线段树+离线
测试地址:影魔
做法:本题需要用到线段树+离线。
首先你需要注意到,题目中所给的序列是一个全排列(没看到这个条件的我直接跪了)。然后我们转化题目中的限制条件,我们发现这个条件等价于:当分别为的最大和次大值时,有的贡献;当中有一个是的最大值,而另一个不是次大值时,有的贡献。
对于点对的贡献,我们考虑两种情况:
一:贡献为的情况。注意到此时,左端点和右端点之间存在一个最大的,那么应该分别是左右距离它最近的比它大的元素。
二:贡献为的情况。注意到此时,左端点和右端点之间存在一个最大的,分类讨论,如果,那么应是向左距离它最近的比它大的元素,又因为到之间没有比大的数,也就是说在向右距离它最近的比它大的元素左边,而可以同理分析。
于是我们发现了一种新的统计贡献的方式:枚举,找到它向左和向右距离它最近的比它大的元素(用单调栈或者的数据结构都可以),按照上面的分析统计贡献。注意到上面的贡献都是一个端点固定,另一个端点在一个连续区间中移动的情况,所以我们可以把这些贡献看成固定点对移动点的贡献。又想到一个区间中所有点对的总贡献,等于区间中的点对该区间的总贡献,减去区间中的点对该区间的总贡献,所以我们离线,再配合线段树维护即可。于是我们就解决了这一题,时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,a[200010],l[200010],r[200010],st[200010],top,tot;
int ql[200010],qr[200010];
ll p1,p2,seg[800010]={0},tag[800010]={0},ans[200010];
bool vis[200010]={0};
struct oper
{
int pos,l,r;
ll c;
}p[600010];
struct event
{
int id,pos;
}q[400010];
bool cmp(oper a,oper b) {return a.pos<b.pos;}
bool cmpe(event a,event b) {return a.pos<b.pos;}
void insert(int pos,int l,int r,ll c)
{
p[++tot].pos=pos;
p[tot].l=l,p[tot].r=r;
p[tot].c=c;
}
void init()
{
scanf("%d%d%lld%lld",&n,&m,&p1,&p2);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
top=0;
for(int i=1;i<=n;i++)
{
while(top&&a[st[top]]<a[i])
{
r[st[top]]=i;
top--;
}
if (top) l[i]=st[top];
else l[i]=0;
st[++top]=i;
}
while(top)
{
r[st[top]]=n+1;
top--;
}
tot=0;
for(int i=1;i<=n;i++)
{
if (l[i]!=0&&r[i]!=n+1&&l[i]!=r[i]) insert(l[i],r[i],r[i],p1);
if (l[i]!=0&&i+1<=r[i]-1) insert(l[i],i+1,r[i]-1,p2);
if (r[i]!=n+1&&l[i]+1<=i-1) insert(r[i],l[i]+1,i-1,p2);
}
sort(p+1,p+tot+1,cmp);
}
void pushdown(int no,ll l,ll r)
{
if (tag[no]!=0)
{
ll mid=(l+r)>>1;
tag[no<<1]+=tag[no],tag[no<<1|1]+=tag[no];
seg[no<<1]+=(mid-l+1)*tag[no];
seg[no<<1|1]+=(r-mid)*tag[no];
tag[no]=0;
}
}
void pushup(int no)
{
seg[no]=seg[no<<1]+seg[no<<1|1];
}
void add(int no,ll l,ll r,int s,int t,ll c)
{
if (l>=s&&r<=t)
{
tag[no]+=c;
seg[no]+=(r-l+1)*c;
return;
}
ll mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) add(no<<1,l,mid,s,t,c);
if (t>mid) add(no<<1|1,mid+1,r,s,t,c);
pushup(no);
}
ll query(int no,ll l,ll r,int s,int t)
{
if (l>=s&&r<=t) return seg[no];
ll sum=0,mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) sum+=query(no<<1,l,mid,s,t);
if (t>mid) sum+=query(no<<1|1,mid+1,r,s,t);
return sum;
}
void work()
{
for(int i=1;i<=m;i++)
{
int x=(i<<1)-1,y=(i<<1);
scanf("%d%d",&ql[i],&qr[i]);
q[x].pos=ql[i]-1,q[y].pos=qr[i];
q[x].id=q[y].id=i;
}
sort(q+1,q+(m<<1)+1,cmpe);
int now=1,nowa=1;
for(int i=1;i<=(m<<1);i++)
{
while(nowa<=q[i].pos) add(1,1,n,nowa+1,nowa+1,p1),nowa++;
while(now<=tot&&p[now].pos<=q[i].pos)
{
add(1,1,n,p[now].l,p[now].r,p[now].c);
now++;
}
if (!vis[q[i].id])
{
ans[q[i].id]=query(1,1,n,ql[q[i].id],qr[q[i].id]);
vis[q[i].id]=1;
}
else ans[q[i].id]=query(1,1,n,ql[q[i].id],qr[q[i].id])-ans[q[i].id];
}
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
}
int main()
{
init();
work();
return 0;
}