题目传送门
题目大意:
连续的区间查询
解题过程(因为之间发表在了luogu的论坛上,现在整合过来而已):
1、马上想到了单调队列的思路;
2、最近在磕线段树,反正手速快,先跑一遍线段树。
先写线段树的白痴思路1:
1、建空树的时候,在叶子的时候顺便读初始值(省掉插初始值的函数和运算时间),建好就已经不是空树了。
2、直接写查询最大值函数,然后复制一份,改成最小值。
3、输出。
结果:50分,囧。
自己分析:感觉分大小值,查两遍,时间复杂度翻倍了。
↓↓↓↓↓↓↓↓↓=还有这种操作=↓↓↓↓↓↓↓↓↓
果断去翻题解:有大神说只需要查一次,每次查的时候同时返回最大和最小值!!
↑↑↑↑↑↑↑↑↑=还有这种操作=↑↑↑↑↑↑↑↑↑
机智的我,想到了返回一个结构体,完美。
结果:80分,t最后2个点。
↓↓↓↓↓↓↓↓↓=还有这种操作=↓↓↓↓↓↓↓↓↓
回去认真再看了一次大神的分析,原来过程先输出最小值,最后单独输出最大值。又省掉一个百万级别的循环。
↑↑↑↑↑↑↑↑↑=还有这种操作=↑↑↑↑↑↑↑↑↑
然后终于 A 了,一会再刷一次单调队列,现在先上线段树的白痴代码。
#include<cstdio>
struct nod{int l,r,s1,s2,a,b;nod(){a=-999999999,b=999999999;}}a[2000010];
int n,k,len=0;
struct nod2{int a,b;}b[2000010];//存答案的结构体
int minn(int x,int y) { return x<y?x:y; }
int maxx(int x,int y) { return x>y?x:y; }
void br(int l,int r)
{
len++; int x=len; a[x].l=l; a[x].r=r;
if(l==r) { scanf("%d",&a[x].a); a[x].b=a[x].a; return;} //到叶子了,读数据
if(l<r)
{
int m=(l+r)/2;
a[x].s1=len+1; br(l,m);
a[x].s2=len+1; br(m+1,r);
}
a[x].a=maxx(a[a[x].s1].a,a[a[x].s2].a);//a 最大值
a[x].b=minn(a[a[x].s1].b,a[a[x].s2].b);//b 最小值
}
nod2 cha(int x,int l,int r)
{
nod2 p; int ma=-999999999,mi=999999999;
//p作为缓存结构体, ma 存临时最大值,mi存临时最小值
if(a[x].l==l&&a[x].r==r) { p.a=a[x].a; p.b=a[x].b; return p;}
int m=(a[x].l+a[x].r)/2,s1=a[x].s1,s2=a[x].s2;
if(r<=m) return cha(s1,l,r);
else if(l>m) return cha(s2,l,r);
else
{ p=cha(s1,l,m); ma=maxx(ma,p.a);mi=minn(mi,p.b);
p=cha(s2,m+1,r); ma=maxx(ma,p.a);mi=minn(mi,p.b);
p.a=ma; p.b=mi; return p;
}
}
int main()
{
scanf("%d %d",&n,&k);
br(1,n);
for(int i=1;i<=n-k+1;i++)
{
int j=i+k-1;
b[i]=cha(1,i,j);
printf("%d ",b[i].b);//第一行输出放在这里,省掉一次百万级的循环,拿下最后2个点
}
printf("\n");
for(int i=1;i<=n-k+1;i++) printf("%d ",b[i].a);
return 0;
}
下面是用两个单调队列来完成的代码(简单粗暴):
#include<cstdio>
#include<cstring>
int n,l,la=0,lb=0;
int a[1000010],an[1000010],bn[1000010];
struct nod{int x,i;} ;
nod da[1000010];//下降队列,头最大 :解决最大值
int t1=1,w1=1;//队列的头尾标签
void pp1(int l,int x)//最大值
{
//进队列
while(w1>=t1&&a[x]>da[w1].x) w1--;//找到插入的位置
w1++;//插入
da[w1].x=a[x];
da[w1].i=x;
//出队列(区间之前位置的数)
while(t1<=w1 &&da[t1].i<l) t1++;
}
//==========================================================
nod db[1000010];//上升队列,头最小:解决最小值
int t2=1,w2=1;// 头尾标签
void pp2(int l,int x)//最小值
{
//进队列
while(w2>=t2&&a[x]<db[w2].x) w2--;//找到插入的位置
w2++;//插入
db[w2].x=a[x];
db[w2].i=x;
//出队列(区间之前位置的数)
while(t2<=w2 &&db[t2].i<l) t2++;
}
int main()
{
scanf("%d %d",&n,&l);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
da[1].x=-999999999;
db[1].x= 999999999;
for(int i=1;i<=l;i++)// 第一个区间
{
pp1(1,i);
pp2(1,i);
}
an[++la]=da[t1].x;
bn[++lb]=db[t2].x;
printf("%d ",bn[lb]);
for(int i=2;i<=n-l+1;i++)//后续的区间
{
int j=i+l-1;
pp1(i,j); an[++la]=da[t1].x;
pp2(i,j); bn[++lb]=db[t2].x; printf("%d ",bn[lb]);
}
printf("\n");
for(int i=1;i<=la;i++) printf("%d ",an[i]);
return 0;
}