HNOI2017 影魔

版权声明:随意转载,愿意的话提一句作者就好了 https://blog.csdn.net/stone41123/article/details/84134286

Link

Diffculty

算法难度6,思维难度7,代码难度6

Description

给定一个长度为 n n 的排列 A A

对于一个区间 [ L , R ] ( L < R ) [L,R](L<R) ,如果 m a x ( A L + 1 , A L + 2 , , A R 1 ) < m i n ( A L , A R ) max(A_{L+1},A_{L+2},…,A_{R-1})<min(A_L,A_R) ,那么它的价值为 p 1 p1

如果 m a x ( A L + 1 , A L + 2 , , A R 1 ) max(A_{L+1},A_{L+2},…,A_{R-1}) 小于 A L A_L A R A_R 中的其中一个,那么它的价值为 p 2 p2

否则区间价值为 0 0

m m 个询问,给定 [ a , b ] [a,b] ,询问 a L < b , a < R b a\le L<b,a<R\le b 的所有区间价值和。

1 n , m 2 × 1 0 5 , 1 p 1 , p 2 1000 1\le n,m\le 2\times 10^5,1\le p1,p2\le 1000

Solution

神仙题。。。

我们考虑把每个区间放在它的最大值处统计。

首先我们求出第 i i 个数左右边第一个大于它的值的位置 L e f t i , R i g h t i Left_i,Right_i

那么区间 [ L e f t i , R i g h t i ] [Left_i,Right_i] 满足 p 1 p1 的价值。

假如固定左端点的话,区间 [ L e f t i , k ] , i < k < R i g h t i [Left_i,k],i<k<Right_i 都满足 p 2 p2 了。

同理 [ k , R i g h t i ] , L e f t i < k < i [k,Right_i],Left_i<k<i 也都满足 p 2 p2 了。

我们考虑一旦左端点越过 L e f t i Left_i ,那么就不符合把区间放在最大值处统计了,右端点同理。

这样我们可以做到统计所有合法区间而且不统计不合法区间,也就是不重不漏了。

现在,我们考虑如何利用推出的这些东西来计算答案。

我们考虑建立二维平面, ( x , y ) (x,y) 这个点代表 [ x , y ] [x,y] 这个区间。

那么上面的那三个部分就都可以表示为点或者线段了。

询问则可以表示为一个正方形。

我们可以两遍扫描线+线段树出解。

时间复杂度 O ( n l o g n ) O(nlogn)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#define LL long long
using namespace std;
inline int read(){
	int x=0,f=1;char ch=' ';
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9')x=x*10+(ch^48),ch=getchar();
	return f==1?x:-x;
}
const int N=2e5+5;
LL sum[N<<2],add[N<<2],Len[N<<2];
inline void pushdown(int rt){
	if(add[rt]){
		sum[rt<<1]+=add[rt]*Len[rt<<1];
		sum[rt<<1|1]+=add[rt]*Len[rt<<1|1];
		add[rt<<1]+=add[rt];
		add[rt<<1|1]+=add[rt];
		add[rt]=0;
	}
}
inline void build(int rt,int l,int r){
	Len[rt]=r-l+1;
	sum[rt]=add[rt]=0;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
}
inline void modify(int rt,int l,int r,int L,int R,int v){
	if(L>R)return;
	if(L<=l && r<=R){
		sum[rt]+=v*Len[rt];
		add[rt]+=v;
		return;
	}
	pushdown(rt);
	int mid=(l+r)>>1;
	if(L<=mid)modify(rt<<1,l,mid,L,R,v);
	if(mid+1<=R)modify(rt<<1|1,mid+1,r,L,R,v);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
inline LL query(int rt,int l,int r,int L,int R){
	if(L<=l && r<=R)return sum[rt];
	pushdown(rt);
	int mid=(l+r)>>1;LL ans=0;
	if(L<=mid)ans+=query(rt<<1,l,mid,L,R);
	if(mid+1<=R)ans+=query(rt<<1|1,mid+1,r,L,R);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
	return ans;
}

struct data{
	int id,x,y1,y2,v;
	inline bool operator < (const data& b) const {
		if(x==b.x)return id<b.id;
		return x<b.x;
	}
}q[N<<2],q2[N<<2];
LL ans[N],p1,p2;
int n,m,cnt,cnt2;
int a[N],Log[N],st[N][18];
int Left[N],Right[N];
int main(){
	n=read();m=read();p1=read();p2=read();
	for(int i=1;i<=n;++i)a[i]=read(),st[i][0]=a[i];
	a[0]=st[0][0]=1e9;
	a[n+1]=st[n+1][0]=1e9;
	Log[0]=-1;
	for(int i=1;i<=n+2;++i)Log[i]=Log[i>>1]+1;
	for(int k=1;k<=17;++k)
		for(int i=0;i+(1<<k)-1<=n+1;++i)
			st[i][k]=max(st[i][k-1],st[i+(1<<(k-1))][k-1]);
	for(int i=1;i<=n;++i){
		int l=0,r=i-1,mid;
		while(l<r){
			mid=(l+r)>>1;
			int k=Log[i-mid];
			int num=max(st[mid+1][k],st[i-(1<<k)+1][k]);
			if(num>a[i])l=mid+1;
			else r=mid;
		}
		Left[i]=l;
		l=i+1,r=n+1;
		while(l<r){
			mid=(l+r)>>1;
			int k=Log[mid-i+1];
			int num=max(st[i][k],st[mid-(1<<k)+1][k]);
			if(num>a[i])r=mid;
			else l=mid+1;
		}
		Right[i]=l;
		q[++cnt]=(data){0,Left[i],Right[i],Right[i],p1};
		q[++cnt]=(data){0,Left[i],i+1,Right[i]-1,p2};
		q2[++cnt2]=(data){0,Right[i],Left[i]+1,i-1,p2};
	}
	for(int i=1;i<=m;++i){
		int a=read(),b=read();
		ans[i]=p1*(b-a);
		q[++cnt]=(data){i,a-1,a,b,-1};
		q[++cnt]=(data){i,b,a,b,1};
		q2[++cnt2]=(data){i,a-1,a,b,-1};
		q2[++cnt2]=(data){i,b,a,b,1};
	}
	sort(q+1,q+cnt+1);
	sort(q2+1,q2+cnt2+1);
	build(1,0,n+1);
	for(int i=1;i<=cnt;++i){
		if(q[i].id)ans[q[i].id]+=query(1,0,n+1,q[i].y1,q[i].y2)*q[i].v;
		else modify(1,0,n+1,q[i].y1,q[i].y2,q[i].v);
	}
	build(1,0,n+1);
	for(int i=1;i<=cnt2;++i){
		if(q2[i].id)ans[q2[i].id]+=query(1,0,n+1,q2[i].y1,q2[i].y2)*q2[i].v;
		else modify(1,0,n+1,q2[i].y1,q2[i].y2,q2[i].v);
	}
	for(int i=1;i<=m;++i)printf("%lld\n",ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/stone41123/article/details/84134286