L. Continuous Intervals解题报告

题干

codeforce
拉米斯是一个聪明的女孩。她对关于数列及其区间的问题很感兴趣。

这里她给你看了一个长度为 \(n\) 的数列,里面全是正整数,记作 \(a_1, a_2, a_3, ⋯, a_n\)。她对那些区间感到非常惊讶,这些区间是 \(a_1, a_2, ⋯, a_n\) 的连续子序列,由几个连续的数字组成,她称它们为连续区间。

更确切地说,考虑一个区间 \(a_l, a_{l+1}, ⋯, a_{r−1}, a_r\),其中 \(1≤l≤r≤n\)。如果在对该区间排序后,任意两个相邻元素的差都小于或等于 \(1\),那么这个区间就被认为是连续的。

作为她最好的朋友,你们从四面八方赶来,跋山涉水来到宁夏,帮助她计算这个数列中连续区间的数量。

前言

听说是个trick,但我确实是想不到,但真理解了思路确实挺妙。

扫描线思想

可以发现对于一个区间\([l,r]\)当且仅当满足下面的式子,区间为连续区间:

$max_{[l,r]}-min_{[l,r]}+1=cnt$
式子中的$cnt$为区间不同的数的数量。

我们可以利用扫描线思想,扫描右端点,并把扫描过的端点当做左端点,并用线段树维护这些左端点对该右端点的贡献,累计贡献即为答案。

关键在于贡献怎么维护。为方便处理可以把上面的式子转化为:

$max_{l,r}-min_{l,r}-cnt=-1$
在线段树上维护所有上面式子为-1的数量即可。

接着观察影响该式子的各项:

max的维护

可以利用单调栈,维护一个单调递减的栈(栈中是一个一个的区间块,该区间块上所有点到当前右端点的最大值相同),当加入一个新的端点,比较大小关系,若该点改变了前一个区间块的最大值,就在线段树上进行区间更新,更新大小为最大值的变化量。

min的维护

同理可以用单调栈,维护一个单调递增的栈,更新量为最小值的变化量。

cnt的维护

可以离散化或用 map 来记录上一次各值出现的位置(初始化为零),每加入一个端点,就更新上一次出现位置的后一位到该点位置,更新量为 -1 (\(cnt\)增加1)

统计答案

可以发现若区间不满足连续区间性质,则一定有:

$max_{l,r}-min_{l,r}-cnt>-1$
而区间$[r,r]$一定满足条件,则只需要维护上面式子的最小值的数量即可。

因为一定有解,所以根节点最小值一定为 -1 ,根节点统计的数量即为答案。

code

#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<vector>
#include<deque>
#include<queue>
#include<iomanip>
#include<bitset>
#include<unordered_map>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}
const int maxn=1e5+5;
int t,n;
struct nd
{
	int l,r,cnt,mi,tag;
}v[maxn<<2];
void build(int rt,int l,int r)
{
	v[rt].l=l,v[rt].r=r;v[rt].tag=0;v[rt].mi=0;
	if(l==r)
	{
		v[rt].cnt=1;//叶子最小值一定是它本身
		return;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid),build(rson,mid+1,r);
}
void pushup(int rt)
{
	if(v[lson].mi==v[rson].mi)
	{
		v[rt].mi=v[lson].mi;
		v[rt].cnt=v[lson].cnt+v[rson].cnt;
	}
	else if(v[lson].mi<v[rson].mi)
	{
		v[rt].mi=v[lson].mi;
		v[rt].cnt=v[lson].cnt;
	}
	else
	{
		v[rt].mi=v[rson].mi;
		v[rt].cnt=v[rson].cnt;
	}
}
void pushdown(int rt)
{
	if(v[rt].tag)
	{
		int lz=v[rt].tag;
		v[rt].tag=0;
		v[lson].tag+=lz,v[rson].tag+=lz;
		v[lson].mi+=lz,v[rson].mi+=lz;
	}
}
void update(int rt,int L,int R,int val)
{
	if(v[rt].l>=L&&v[rt].r<=R)
	{
		v[rt].mi+=val;
		v[rt].tag+=val;
		return;
	}
	pushdown(rt);
	int mid=(v[rt].l+v[rt].r)>>1;
	if(L<=mid) update(lson,L,R,val);
	if(R>mid) update(rson,L,R,val);
	pushup(rt);
}
pair<int,int> skma[maxn],skmi[maxn];
int topma,topmi;
unordered_map<int,int> q;
signed main()
{
	t=read();
	for(int ss=1;ss<=t;ss++)
	{
		n=read();
		build(1,1,n);
		long long ans=0;
		q.clear();topmi=topma=0;
		for(int i=1,op;i<=n;i++)
		{
			op=read();
			int last=i;
			while(topma&&op>=skma[topma].first)
			{
				update(1,skma[topma].second,last-1,op-skma[topma].first);
				last=skma[topma].second;
				topma--;
			}
			skma[++topma]={op,last};//维护max影响
			last=i;
			while(topmi&&op<=skmi[topmi].first)
			{
				update(1,skmi[topmi].second,last-1,skmi[topmi].first-op);
				last=skmi[topmi].second;
				topmi--;
			}
			skmi[++topmi]={op,last};//维护min
			if(q[op]) update(1,q[op]+1,i,-1);
			else update(1,1,i,-1);
			q[op]=i;//维护cnt
			ans+=v[1].cnt;//统计答案
		}
		printf("Case #%d: %lld\n",ss,ans);
	}
	return 0;
}
posted @ 2026-07-19 10:32  wang7979  阅读(11)  评论(0)    收藏  举报