【题解】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大佬除外!)
浙公网安备 33010602011771号