! AHOI/HNOI2017影魔

区间新解法


\(n,m2e5\)

SOL:

\(L[i],R[i]\)分别表示\(i\)左边和右边第一个比自己大的位置

  1. \((L[i],R[i])\)产生\(p1\)贡献,在\(R[i]\)时刻对\(L[i]\)更新
  2. \((L[i]+1\to i-1,R[i])\)产生\(p2\)贡献,在\(R[i]\)时刻对\(L[i]+1\to i-1\)更新
  3. \((L[i],i+1\to R[i]-1)\)产生\(p1\)贡献,在\(L[i]\)时刻对\(i+1\to R[i]-1\)更新

\(l-1\)查询\(l,r\)答案\(ans1\)

\(r\)查询\(l,r\)答案\(ans2\)

\(ans=ans1-ans2\)(仔细想一想)

神奇的区间求法:若固定一端,另一端为一段区间可采用此方法!!

为什么不会算重?

每次找到一个最大值,相当于把序列分成两半,相互不影响

时间复杂度\(O(nlog_n)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=2e5+4;
struct ques{
	int t,l,r,x,fl;
}q[N*5];
inline bool comp(const ques &a,const ques &b){
	return a.t==b.t?(!a.fl):a.t<b.t;
}
int n,m,p1,p2,tot,a[N],L[N],R[N],t1[N],t2[N],st[N],ans[N];
inline void add(int x,int v){
	for(int i=x;i<=n;i+=i&-i){
		t1[i]+=v;t2[i]+=x*v;
	}
}
inline int ask(int x){
	int ret=0;
	for(int i=x;i;i-=i&-i)
		ret+=(x+1)*t1[i]-t2[i];
	return ret;
}
signed main(){
	n=read();m=read();p1=read();p2=read();
	for(int i=1,top=0;i<=n;i++){
		a[i]=read();
		while(top&&a[i]>a[st[top]]){R[st[top]]=i;top--;}
		L[i]=st[top];
		st[++top]=i; 
	}
	for(int i=1;i<=n;i++){
		if(!R[i])R[i]=n+1;
		if(R[i]<=n&&L[i])q[++tot]=(ques){R[i],L[i],L[i],p1,0};
		if(R[i]<=n&&L[i]+1<=i-1)q[++tot]=(ques){R[i],L[i]+1,i-1,p2,0};
		if(L[i]&&i+1<=R[i]-1)q[++tot]=(ques){L[i],i+1,R[i]-1,p2,0};
	}
	for(int i=1,l,r;i<=m;i++){
		l=read();r=read();
		ans[i]+=(r-l)*p1;
		q[++tot]=(ques){l-1,l,r,i,-1};
		q[++tot]=(ques){r,l,r,i,1};
	}
	sort(q+1,q+tot+1,comp);
	for(int i=1;i<=tot;i++){
		if(!q[i].fl){
			add(q[i].l,q[i].x);
			add(q[i].r+1,-q[i].x);
		}
		else{
			ans[q[i].x]+=q[i].fl*(ask(q[i].r)-ask(q[i].l-1));
		}
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<"\n";
	return (0-0);
}
posted @ 2020-03-18 21:00  starusc  阅读(93)  评论(0)    收藏  举报