【题解】P1886 滑动窗口 /【模板】单调队列

菜鸡最近刚学了线段树,碰巧看到滑动窗口这道题,路过并用线段树水了下子

当时计算时间复杂度由于常数比较大,正好卡边,不知道能不能过,于是就试了试

不废话了,读题!

题目链接->洛谷

题目大意:

有一个长度为n的序列,一个长度为k的窗口,窗口从左滑到右

要求每次滑动之后,窗口内序列的最大值和最小值

当然了,这是单调队列的经典模板

可是瞅了瞅,菜鸡已经忘了单调队列咋写了

又想到最近学的线段树,

这这这..!这不维护区间最值吗!

线段树板子貌似都可以直接套上嘛!

可常数可能有点大..算了算了先试试吧!

线段树维护两个关键量,一个是最大值,一个是最小值

显然,父亲节点的最大值,是两个儿子最大值的最大值

父亲节点的最小值,是两个儿子的最小值的最小值

先看一下代码吧

有几个宏定义可能看着怪怪的

写代码的时候也不知道咋想的

#include<iostream>
#include<cstdio>
#define mid tr[x].l+tr[x].r>>1
#define pushup tr[x].maxn=max(tr[x<<1].maxn,tr[x<<1|1].maxn),tr[x].minn=min(tr[x<<1].minn,tr[x<<1|1].minn)
using namespace std;
struct trees{
	int maxn,l,r,minn;
}tr[4000000];
int n,m;
void build(int l,int r,int x){
	tr[x].l=l;tr[x].r=r;
	if(l==r){
		cin>>tr[x].maxn;
		tr[x].minn=tr[x].maxn;
		return ;
	}
	build(l,mid,x<<1);
	build((mid)+1,r,x<<1|1);
	pushup;
}
int maxx,minx;
void query_max(int l,int r,int x){
	if(tr[x].l>=l&&tr[x].r<=r){
		maxx=max(maxx,tr[x].maxn);
		return ;
	}
	if(l<=mid) query_max(l,r,x<<1);
	if(mid<r) query_max(l,r,x<<1|1);
}
void query_min(int l,int r,int x){
	if(tr[x].l>=l&&tr[x].r<=r){
		minx=min(minx,tr[x].minn);
		return ;
	}
	if(l<=mid) query_min(l,r,x<<1);
	if(mid<r) query_min(l,r,x<<1|1);
}
int main(){
	cin>>n;
	cin>>m;
	build(1,n,1);
	for(int i=1;i<=n-m+1;i++){
		minx=(1<<30);
		query_min(i,i+m-1,1);
		cout<<minx<<" ";
	}
	cout<<endl;
	for(int i=1;i<=n-m+1;i++){
		maxx=-(1<<30);
		query_max(i,i+m-1,1);
		cout<<maxx<<" ";
	}
	return 0;
}

也比较幸运,大数据$967ms$,

真·卡边过

貌似不是很稳..

然后加了个快读

代码变成这样子

#include<iostream>
#include<cstdio>
#define mid tr[x].l+tr[x].r>>1
#define pushup tr[x].maxn=max(tr[x<<1].maxn,tr[x<<1|1].maxn),tr[x].minn=min(tr[x<<1].minn,tr[x<<1|1].minn)
using namespace std;
struct trees{
	int maxn,l,r,minn;
}tr[4000000];
int n,m;
inline int read(){
	register int s=0,w=1;
	register char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+(ch^48);
		ch=getchar();
	}
	return w*s;
}
void build(int l,int r,int x){
	tr[x].l=l;tr[x].r=r;
	if(l==r){
		tr[x].maxn=read();
		tr[x].minn=tr[x].maxn;
		return ;
	}
	build(l,mid,x<<1);
	build((mid)+1,r,x<<1|1);
	pushup;
}
int maxx,minx;
void query_max(int l,int r,int x){
	if(tr[x].l>=l&&tr[x].r<=r){
		maxx=max(maxx,tr[x].maxn);
		return ;
	}
	if(l<=mid) query_max(l,r,x<<1);
	if(mid<r) query_max(l,r,x<<1|1);
}
void query_min(int l,int r,int x){
	if(tr[x].l>=l&&tr[x].r<=r){
		minx=min(minx,tr[x].minn);
		return ;
	}
	if(l<=mid) query_min(l,r,x<<1);
	if(mid<r) query_min(l,r,x<<1|1);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	n=read();
	m=read();
	build(1,n,1);
	for(register int i=1;i<=n-m+1;i++){
		minx=(1<<30);
		query_min(i,i+m-1,1);
		cout<<minx<<" ";
	}
	cout<<endl;
	for(register int i=1;i<=n-m+1;i++){
		maxx=-(1<<30);
		query_max(i,i+m-1,1);
		cout<<maxx<<" ";
	}
	return 0;
}

大数据736ms

还是能说的过去吧

那就这样啦

所以线段树这玩意常数太大

在某些显然不适用的题上不要乱用

(大佬们除外)

(如:Refined_heart大佬除外!)

posted on 2021-06-29 20:48  MfslM  阅读(70)  评论(0)    收藏  举报