P1886 P2216 单调队列模板

Posted んцγυfёìfのι

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1886 P2216 单调队列模板相关的知识,希望对你有一定的参考价值。

何为单调队列?

单调队列是一个队列(废话)

而且必须同时满足下标单调和值单调两个单调特性。

跟优先队列不同,优先队列直接使用堆(heap)来实现,如何删去特定下标元素?不明。

本人喜欢用单调队列存下标,这样比存值不知道高到哪里去了。

新来一个元素,进队。之后特判长度有没有超过。超过则把最前面的元素出队。

之后,如果不满足性质,就把前面的元素顶掉,直到满足性质为止。

然后才可以把队首元素拿来用。

有几个很坑的地方,具体看代码注释。

(为什么top是队尾而tail是队首?laughcry)

例题:洛谷P1886,高级模板题。还有一道剧毒的P2216。(排版很奇怪,都怪反人类的博客园?)

AC代码:

技术分享图片
 1 #include <cstdio>
 2 const int N = 1000010;
 3 
 4 int pl[N], l_t, l_h = 1, ansl[N];
 5 int ps[N], s_t, s_h = 1, anss[N];
 6 int a[N];
 7 
 8 int main() {
 9     int n, k;
10     scanf("%d%d", &n, &k);
11     for(int i = 1; i <= n; i++) {
12         scanf("%d", &a[i]);
13     }
14     for(int i = 1; i <= n; i++) {
15         pl[++l_t] = i;
16         ps[++s_t] = i;
17         if(pl[l_h] + k <= i) {
18             l_h++;
19         }
20         if(ps[s_h] + k <= i) {
21             s_h++;
22         }
23         while(l_t > l_h && a[pl[l_t - 1]] <= a[i]) {
24             l_t--;
25         }
26         while(s_t > s_h && a[ps[s_t - 1]] >= a[i]) {
27             s_t--;
28         }
29         pl[l_t] = i;
30         ps[s_t] = i;
31         ansl[i] = pl[l_h];
32         anss[i] = ps[s_h];
33     }
34     for(int i = k; i <= n; i++) {
35         printf("%d ", a[anss[i]]);
36     }
37     printf("\n");
38     for(int i = k; i <= n; i++) {
39         printf("%d ", a[ansl[i]]);
40     }
41     return 0;
42 }
最新写的P1886
技术分享图片
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 const int N=1002;
 5 struct ah{int large,small;}f[N][N];
 6 int c[N][N];
 7 int p1[N],p2[N],top1,tail1=1,top2,tail2=1;///存下标
 8 int a,b,n;///p1求min,p2求max
 9 long long int ans=999999999,s;
10 
11 int main()
12 {
13     scanf ("%d%d%d",&a,&b,&n);
14     for(int i=1;i<=a;i++)
15     {
16         top1=top2=0;
17         tail1=tail2=1;
18         for(int j=1;j<=b;j++)
19         {
20             scanf ("%d",&c[i][j]);
21             p1[++top1]=p2[++top2]=j;
22             while(c[i][p1[top1]]<=c[i][p1[top1-1]] && top1>tail1)
23             {///单调队列维护
24                 p1[top1-1]=p1[top1];
25                 top1--;
26             }
27             while(c[i][p2[top2]]>=c[i][p2[top2-1]] && top2>tail2)
28             {
29                 p2[top2-1]=p2[top2];
30                 top2--;
31             }
32             if(p1[top1]-p1[tail1]>=n) tail1++;///区间长度n
33             if(p2[top2]-p2[tail2]>=n) tail2++;
34             if(j>=n)
35             {///产生最大最小值
36                 f[i][j-n+1].large=c[i][p2[tail2]];
37                 f[i][j-n+1].small=c[i][p1[tail1]];
38             }
39         }
40     }
41     ///读入完毕,处理①完毕
42 /*
43     for(int i=1;i<=a;i++)///调试
44     {
45         for(int j=1;j<=b-n+1;j++)
46         {
47             printf("%d-%d ",f[i][j].large,f[i][j].small);
48         }
49         printf("\n");
50     }*/
51 
52 
53     for(int j=1;j<=b-n+1;j++)
54     {
55         top1=top2=0;
56         tail1=tail2=1;
57         for(int i=1;i<=a;i++)///p1 small  p2 large
58         {
59             p1[++top1]=p2[++top2]=i;
60             while(f[p1[top1]][j].small<=f[p1[top1-1]][j].small && top1>tail1)
61             {
62                 p1[top1-1]=p1[top1];
63                 top1--;
64             }
65             while(f[p2[top2]][j].large>=f[p2[top2-1]][j].large && top2>tail2)
66             {
67                 p2[top2-1]=p2[top2];
68                 top2--;
69             }
70             if(p1[top1]-p1[tail1]>=n) tail1++;
71             if(p2[top2]-p2[tail2]>=n) tail2++;
72             if(i>=n)
73             {///产生最值
74                 s=1ll*(f[p2[tail2]][j].large-f[p1[tail1]][j].small);
75                 //printf("%d ",s);
76                 ans=min(ans,s);
77             }
78 
79         }
80         //printf("\n");
81     }
82     printf("%lld",ans);
83     return 0;
84 }
P2216

博客园排版有剧毒。。。

以上是关于P1886 P2216 单调队列模板的主要内容,如果未能解决你的问题,请参考以下文章

ybtoj 单调队列课堂过关luogu P1886例题1滑动窗口

洛谷P1886 滑动窗口 单调队列

P2216 [HAOI2007]理想的正方形 - 单调队列

[模板]单调队列

P1886 滑动窗口(单调队列)

单调队列入门