【2018宁夏邀请赛 L】Continuous Intervals【线段树】

题意:

n n 个数的一个序列,对于一个连续区间,将这个区间内的数排序,相邻数字差距小于等于 1 1 ,则这个区间符合条件。问这个序列中有多少个符合条件的连续区间。 ( 1 n 1 0 6 ) (1\leq n\leq 10^6)


思路:

这是一个区间计数问题,即询问一个序列中符合条件的区间个数。通常此类问题的思考方向是确定一个左端点,求有多少个符合条件的右端点。或者确定一个右端点,求有多少个符合条件的左端点。

然后我们再思考,如何判断一个区间符合条件。区间符合条件的要求即为 m a x m i n + 1 c n t max-min+1\geq cnt m a x max 为区间中最大的数, m i n min 为区间中最小的数, c n t cnt 为区间中不同大小的数的个数。

然后我们枚举右端点 R R ,查询有多少个 L L ,使得区间 [ L , R ] [L,R] 符合条件。然后线段树中对于每一个位置 p o s pos ,维护 [ p o s , R ] [pos,R] m a x m i n c n t max-min-cnt 的值。对于 m a x max m i n min ,我们可以用单调栈来进行更新,边维护单调栈边更新线段树中每个点的值。对于 c n t cnt ,我们用 m a p map 记录每一个数字上一次出现的位置,然后更新需要更新的区间。

至此我们就只需要维护每一个区间的最小值,以及最小值的个数,可以发现最小值一定大于等于 1 -1 ,因此对于每个 R R 统计答案即可。


代码:

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 1e5+100;
const db EPS = 1e-9;
using namespace std;

void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}

int n,t1,t2;
ll a[N],ans,minn[2*N],num[2*N],lazy[2*N]; //最小值,最小值个数
pair<ll,int> st1[N],st2[N];
map<ll,int> mp;
inline int get_id(int l,int r) {return (l+r) | (l!=r);}

void push_down(int l,int r){
	int now = get_id(l,r), mid = (l+r)>>1;
	int ls = get_id(l,mid), rs = get_id(mid+1,r);
	minn[ls] += lazy[now]; minn[rs] += lazy[now];
	lazy[ls] += lazy[now]; lazy[rs] += lazy[now];
	lazy[now] = 0;
}	

void push_up(int l,int r){
	int now = get_id(l,r), mid = (l+r)>>1;
	int ls = get_id(l,mid), rs = get_id(mid+1,r);
	if(minn[ls] == minn[rs]) minn[now] = minn[ls], num[now] = num[ls]+num[rs];
	else if(minn[ls] < minn[rs]) minn[now] = minn[ls], num[now] = num[ls];
	else minn[now] = minn[rs], num[now] = num[rs];
}

void build(int l,int r){
	int now = get_id(l,r);
	minn[now] = 0, num[now] = r-l+1, lazy[now] = 0;
	if(l == r) return;
	int mid = (l+r)>>1;
	build(l,mid); build(mid+1,r);
}

void update(int l,int r,int p1,int p2,ll c){ //区间修改
	int now = get_id(l,r);
	if(p1 <= l && r <= p2){
		minn[now] += c; lazy[now] += c;
		return;
	}
	if(lazy[now] != 0) push_down(l,r);
	int mid = (l+r)>>1;
	if(p1 <= mid) update(l,mid,p1,p2,c);
	if(p2 > mid) update(mid+1,r,p1,p2,c);
	push_up(l,r);
}

void init(){
	ans = t1 = t2 = 0;
	build(1,n); mp.clear();
}

int main()
{
	int _; scanf("%d",&_);
	rep(Ca,1,_){
		scanf("%d",&n);
		rep(i,1,n) scanf("%lld",&a[i]);
		init();
		rep(i,1,n){
			//处理最大值 —— st1
			int now = i;
			while(t1 > 0 && st1[t1].first < a[i]){
				int pos = st1[t1-1].second;
				update(1,n,pos+1,now-1,-st1[t1].first+a[i]);
				t1--; now = pos+1;
			}
			st1[++t1] = make_pair(a[i],i);
			
			//处理最小值 —— st2
			now = i;
			while(t2 > 0 && st2[t2].first > a[i]){
				int pos = st2[t2-1].second;
				update(1,n,pos+1,now-1,st2[t2].first-a[i]);
				// logs(pos,now-1,st2[t2].first-a[i]);
				t2--; now = pos+1;
			}
			st2[++t2] = make_pair(a[i],i);
			
			//处理cnt
			if(mp.find(a[i]) != mp.end()){
				int pos = mp[a[i]];
				update(1,n,pos+1,i,-1);
			}
			else update(1,n,1,i,-1);
			mp[a[i]] = i;
			
			//计算答案
			if(minn[get_id(1,n)] == -1) ans += num[get_id(1,n)];
		}
		printf("Case #%d: %lld\n",Ca,ans);
	}
	return 0;
}
发布了244 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/100175319
今日推荐