例题两则(不无聊的子序列,HNOI2016序列)

分享例题两则主要是分享一种 trick

UVA1608

题目描述

给定一个长度为 n 的序列 a ,如果 a 的每一个子串都存在至少一个元素只出现了一次,输出 Non-boring 。反之,输出 Boringn2×105

思路点拨

类似于这样的题目,可以按照我下述的方法进行思考。

对于一个元素而言,我们记录上一个与他值相同的元素的下标 prei ,下一个与他元素值相同的下标 suci 。那么对于左端点在 [prei+1,i] 并且右端点在 [i,suci1] 的区间就是满足题目要求的。我们考虑建立一个平面直角坐标系,以区间的左端点作为横坐标,以区间的右端点作为纵坐标。那么一个元素 ai 产生的贡献就可以看作一个以 (prei+1,i) 为左下角,以 (i,suci1) 为右上角的矩形。

如果对于点 (l,r)(1lrn) 都被至少一个矩形覆盖,我们可以判断为有解。现在我们需要使用某种方法来加速这一过程,并且不难想到使用扫描线算法。如果这些矩形的面积并为 n(n+1)2 ,那么我们就认为是有解的。

时间复杂度 O(T×nlogn) ,可以通过。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-f;
		ch=getchar(); 
	} 
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
} 
const int MAXN=4e5+10;
int T,n,a[MAXN];
map<int,int> pre,suc;
int lef[MAXN],rig[MAXN];
struct node{
	int l,r,h,w;
	bool friend operator<(const node &A,const node &B){
		return A.h<B.h;
	}
}line[MAXN];
struct seg{
	int l,r;
	int sum,len;
}t[MAXN<<3];
void pushup(int i){
	if(t[i].sum) t[i].len=t[i].r-t[i].l+1;
	else t[i].len=t[i<<1].len+t[i<<1|1].len;
}
void build(int i,int l,int r){
	t[i].l=l,t[i].r=r;
	t[i].sum=t[i].len=0;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
}
void update(int i,int l,int r,int w){
	if(l<=t[i].l&&t[i].r<=r){
		t[i].sum+=w;
		pushup(i);
		return ;
	}
	if(t[i].l>r||t[i].r<l) return ;
	update(i<<1,l,r,w);
	update(i<<1|1,l,r,w);
	pushup(i);
}
signed main(){
	T=read();
	while(T--){
		n=read();
		for(int i=1;i<=n;i++) a[i]=read();
		pre.clear(),suc.clear();
		for(int i=1;i<=n;i++){
			lef[i]=pre[a[i]];
			if(!lef[i]) lef[i]=0;
			pre[a[i]]=i;
		}
		for(int i=n;i;i--){
			rig[i]=suc[a[i]];
			if(!rig[i]) rig[i]=n+1;
			suc[a[i]]=i;
		}
		for(int i=1;i<=n;i++){
			//(lef[i]+1,i),(i,rig[i]-1)
			line[(i<<1)-1].l=line[i<<1].l=lef[i]+1;
			line[(i<<1)-1].r=line[i<<1].r=i;
			line[(i<<1)-1].h=i,line[i<<1].h=rig[i];
			line[(i<<1)-1].w=1,line[i<<1].w=-1;
		}
		sort(line+1,line+n*2+1);
		build(1,1,n);
		int ans=0,pos=1;
		for(int i=1;i<=n;i++){
			while(pos<=n*2&&line[pos].h<=i){
				update(1,line[pos].l,line[pos].r,line[pos].w);
				pos++;
			}
			ans+=t[1].len;
		}
		if(ans==n*(n+1)/2) cout<<"non-boring"<<endl;
		else cout<<"boring"<<endl;
	} 
	return 0;
}

Luogu3246

题目描述

给定一个长度为 n 的序列 a 。有 m 个询问,每一次询问给出 l,r ,问:L=lrR=Lr(mini=LRai) 的值。其中 n,m105

思路点拨

相信大家看到上面的题目之后,看这题已经是比较简单的了。我们考虑使用单调栈求出对于下标 i ,左边第一个小于等于 ai 的元素下标 prei ,右边第一个严格小的元素下标 suci 。那么对于左端点在 [prei+1,i] 并且右端点在 [i,suci1] 的区间,最小值就是 ai 。我们可以建立平面直角坐标系,以 l 为横轴, r 为纵轴,那么一个元素产生的贡献就是一个矩阵。并且这些矩阵互补相交,构成了一个三角形。可能比较抽象,就拿样例来举例子:

5 2 4 1 3

我们构建出来的矩阵就是:

0 0 0 0 3
0 0 0 1 1
0 0 4 1 1
0 2 2 1 1
5 2 2 1 1

每一次查询 l,r 就等价于查询矩阵 (l,l),(r,r) 的元素和。至此,问题已经豁然开朗了。

我们需要编写一种数据结构,支持:

  • 矩阵加一个数

  • 矩阵求和

这不是随便写吗。

posted @   Diavolo-Kuang  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
点击右上角即可分享
微信分享提示