luogu1886:滑动的窗口((线段树+输出优化)|| 单调队列)

本文探讨了解决连续区间查询问题的两种方法:线段树与单调队列。通过逐步分析与实践,展示了如何优化算法以提高效率,包括减少不必要的循环和并行查询最大最小值等技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目传送门

题目大意:

    连续的区间查询

解题过程(因为之间发表在了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;
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值