Codeforces-689 D Friends and Subsequences(RMQ+二分)




http://codeforces.com/problemset/problem/689/D







题目大意:

给两个序列a,b   问有多少个相同的区间 在这个区间内a序列的最大值等于b序列的最小值 




分析:

首先要解决区间问题  当然用RMQ就行  预处理O(n*log(n))    询问O(1)      然后就是要找有多少个区间满足要求了

直接暴力显然不可以  分析一下区间特性 一个区间分以下三种情况

1、a的 max  >  b 的min        这时候应该减小这个区间 这样max才有可能变小,min才有可能变大

2、a的 max  <  b 的min        这时候应该增大这个区间 这样max才有可能变大,min才有可能变小

3、a的 max  ==  b 的min     这时候就无所谓了 增大或者减小区间都可以 只要max还等于min就行

根据以上三条 我们可以采取固定左区间断点 二分找右区间端点 找一个a的 max  ==  b 的min的最大端点  再找一个

a的 max  ==  b 的min的最小端点  然后这个最大与最小端点之间的所有值都可以和左端点构成一个合法区间 然后累加

以下最大端点和最小端点的差就是答案






AC代码:

#include <bits/stdc++.h>
#define mset(a,x) memset(a,x,sizeof(a))
typedef long long LL;
using namespace std;
LL maxn[200005][25];
LL minn[200005][25];
LL a[200005],b[200005];
int n;
void init(){
	for (int j=1;j<20;j++){
		for (int i=1;i+(1<<j)<=n+1;i++){
			maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
			minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
		}
	}
}
LL rmqmax(int l,int r){
	int k=log(r-l+1)/log(2);
    return max(maxn[l][k],maxn[r-(1<<k)+1][k]);
}
LL rmqmin(int l,int r){
	int k=log(r-l+1)/log(2);
    return min(minn[l][k],minn[r-(1<<k)+1][k]);
}
int main (){
	while (scanf ("%d",&n)!=EOF){
		mset(a,0);mset(b,0);mset(maxn,0);mset(minn,0);
		for (int i=1;i<=n;i++) scanf ("%lld",&a[i]),maxn[i][0]=a[i];
		for (int j=1;j<=n;j++) scanf ("%lld",&b[j]),minn[j][0]=b[j];
		init();
		int l,r;
		LL sum=0;

		for (int i=1;i<=n;i++){
			int tl,tr;
			tl=i,tr=n;
			int indexr=0,indexl=0;
			while (tl<=tr){//   求最大右端点
				int mid=(tl+tr)/2;
				LL tmax=rmqmax(i,mid);
				LL tmin=rmqmin(i,mid);
				if(tmin==tmax) tl=mid+1,indexr=mid;
				else if (tmin>tmax) tl=mid+1;
				else tr=mid-1;
			}
			if (indexr) {
				tl=i,tr=n;
				while (tl<=tr){//   求最小右端点
					int mid=(tl+tr)/2;
					LL tmax=rmqmax(i,mid);
					LL tmin=rmqmin(i,mid);
					if(tmin==tmax) tr=mid-1,indexl=mid;
					else if (tmin>tmax) tl=mid+1;
					else tr=mid-1;
				}
				sum+=indexr-indexl+1;
			}
		}
		printf ("%lld\n",sum);
	}
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值