「CF689D Friends and Subsequences」

题目大意

给出两个长度为 (n) 的序列 (a)(b),计算有多少对 (l,r) 满足 (max{a_l,a_{l+1},dots,a_{r-1},a_r}=min{b_l,b_{l+1},dots,b_{r-1},b_r}).

分析

考虑 (max)((min))的一些性质,(max{a_l,a_{l+1},dots,a_{r-1},a_r}leqmax{a_l,a_{l+1},dots,a_r,a_{r+1}})((min{b_l,b_{l+1},dots,b_{r-1},b_r}geqmin{b_l,b_{l+1},dots,b_r,b_{r+1}})),那么就可以发现这个东西是单调的,所以就很容易想到去二分查找.枚举左端点,查找右端点,可以发现符合条件的右端点肯定是一段区间(可能没有),那么可以二分出这个合法区间的左端点和右端点,然后就可以直接计算贡献了,考虑到取药计算区间最值,所以可以用st表维护.

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
const int MAXN=3e5+7;
int n;
int a[MAXN];
int log_2[MAXN];
int lrmax[MAXN][24];
int lrmin[MAXN][24];
int QueryMax(int left,int right)//查询区间最大值
{
	int len=log_2[right-left+1];
	return max(lrmax[left][len],lrmax[right-(1<<len)+1][len]);
}
int QueryMin(int left,int right)//查询区间最小值
{
	int len=log_2[right-left+1];
	return min(lrmin[left][len],lrmin[right-(1<<len)+1][len]);
}
int main()
{
	scanf("%d",&n);
	REP(i,1,n)
	{
		scanf("%d",&lrmax[i][0]);
	}
	REP(i,1,n)
	{
		scanf("%d",&lrmin[i][0]);
	}
	int now=0;
	REP(i,1,n)//预处理log2(向下取整)
	{
		if(i==(1<<(now+1)))
		{
			++now;
		}
		log_2[i]=now;
	}
	REP(i,1,now)//预处理st表
	{
		REP(j,1,n+1-(1<<i))
		{
			lrmax[j][i]=max(lrmax[j][i-1],lrmax[j+(1<<(i-1))][i-1]);
			lrmin[j][i]=min(lrmin[j][i-1],lrmin[j+(1<<(i-1))][i-1]);
		}
	}
	int left,right,middle,l_appear,r_appear;
	long long answer=0;//注意开long long
	REP(i,1,n)
	{
		left=i;
		right=n;
		l_appear=0;
		r_appear=0;
		while(left<=right)//二分右端点
		{
			middle=(left+right)>>1;
			if(QueryMax(i,middle)>QueryMin(i,middle))//如果max大了那么就减小
			{
				right=middle-1;
			}
			else
			{
				if(QueryMax(i,middle)==QueryMin(i,middle))//如果max和min相等则记录位置
				{
					r_appear=middle;
				}
				left=middle+1;
			}
		}
		left=i;
		right=n;
		while(left<=right)//同理二分出左端点
		{
			middle=(left+right)>>1;
			if(QueryMax(i,middle)>=QueryMin(i,middle))
			{
				if(QueryMax(i,middle)==QueryMin(i,middle))
				{
					l_appear=middle;
				}
				right=middle-1;
			}
			else
			{
				left=middle+1;
			}
		}
		if(l_appear&&r_appear)//如果存在则计算贡献
		{
			answer+=(r_appear-l_appear+1);
		}
	}
	printf("%lld",answer);
	return 0;
}
原文地址:https://www.cnblogs.com/Sxy_Limit/p/12671605.html