[luogu] P1440 求m区间内的最小值

题目描述

一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。

输入输出格式

输入格式:

 

第一行两个数n,m。

第二行,n个正整数,为所给定的数列。

 

输出格式:

 

n行,第i行的一个数ai,为所求序列中第i个数前m个数的最小值。

 

输入输出样例

输入样例#1: 
6 2
7 8 1 4 3 2
输出样例#1: 
0
7
7
1
1
3 

说明

【数据规模】

m≤n≤2000000

很显而易见的板子题。想练练RMQ-ST

但是MAXN = 2000000,用没有滚动数组RMQ-ST会MLE两个点。。。

 1 #include 
 2 #include 
 3 using namespace std;
 4 const int N = 2000010;
 5 
 6 int a[N];
 7 
 8 int mn[N][25];
 9 
10 int n, m, q, l, r;
11 
12 struct RMQ {
13     int log2[N];
14     void init() { 
15         for (int i = 0; i <= n; ++ i) log2[i] = (i == 0 ? -1 : log2[i >> 1] + 1);
16         for (int j = 1; j < 20; ++ j)
17             for(int i = 1; i + (1 << j) - 1 <= n; ++ i)
18                 mn[i][j] = min(mn[i][j - 1], mn[i + (1 << j - 1)][j - 1]);
19     }
20     int query(int ql, int qr) { 
21         int k = log2[qr - ql + 1];
22         return min(mn[ql][k], mn[qr - (1 << k) + 1][k]);
23     }
24 }rmq;
25 
26 void work(){
27     rmq.init();
28     for (int i = 1; i <= n; ++ i) {
29         if (i == 1) { printf("0\n"); continue; }
30         r = i - 1;
31         if (i - m <= 0) l = 1;
32         else l = i - m;
33         printf("%d\n", rmq.query(l, r));
34     }
35 }
36 
37 int main(){
38     scanf("%d%d", &n, &m);
39     for(int i = 1; i <= n; ++ i) scanf("%d", a + i), mn[i][0] = a[i];
40     work();
41     return 0;
42 }

然而懒得写不会滚动数组

所以用了简洁明了的单调队列

 1 #include
 2 #include
 3 #include
 4 #include
 5 #include
 6 
 7 using namespace std;
 8 
 9 const int MAXN = 2000010;
10 int head,tail;
11 int n,k,a[MAXN];
12 
13 struct node {
14     int data,id;
15 } change;
16 node que[MAXN];
17 
18 inline int read() {
19     int num = 0, f = 1; char ch = getchar();
20     while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
21     while (ch >= '0' && ch <= '9') { num = num * 10 + ch - '0'; ch = getchar(); }
22     return num * f;
23 }
24 
25 int main() {
26     n = read(); k = read();
27     for (int i = 1; i <= n; ++ i) a[i] = read(); 
28     puts("0");
29     change.data = a[1];
30     change.id = 1;
31     que[tail++] = change;
32     for (int i = 2; i <= n; ++ i) {
33         if (que[head].id < i - k) head++;
34         printf("%d\n",que[head].data);
35         while (head < tail && a[i] <= que[tail-1].data) tail--;
36         change.id = i;
37         change.data = a[i];
38         que[tail++] = change;
39     }
40     return 0;
41 } 

下面发一波滚动数组版的看码风就知道不是我写的

 1 #include 
 2 
 3 using namespace std;
 4 
 5 const int maxn = 2000005;
 6 int a[maxn],bp,n;
 7 int f[maxn][2]; 
 8 int g[maxn];
 9 
10 int main() 
11 {
12     scanf("%d %d", &n,&bp);
13     for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
14     //init
15     int now=0;
16     for (int i=1;i<=n;i++)
17     {
18         if ((1 << (now+1))<=i) ++now;
19         g[i]=now;
20     }
21     int t = 0;
22     printf("0\n");
23     now = 2;
24     for (int j = 0; (1 << j) <= bp; ++j) 
25     {
26         t = 1 - t;
27         if (j == 0) 
28         {
29             for (int i = 1; i <= n; ++i) f[i][t] = a[i];
30         }
31         else 
32         {
33             for (int i = 1; i <= n - (1 << j) + 1; ++i) 
34             {
35                 f[i][t] = min(f[i][1 - t], f[i + (1 << (j - 1))][1 - t]);
36             }
37         }
38         while (now <= n && min(now - 1, bp) < (1 << (j + 1))) 
39         {
40             int l=now-bp;
41             if (l<=0) l=1;
42             int r=now-1;
43             int x = g[r - l + 1];
44             int ans = min(f[l][t], f[r - (1 << x) + 1][t]);
45             printf("%d\n",ans);
46             ++now;
47         }
48     }
49     return 0;
50 }

比较神奇的是,ZKW线段树也不会T掉看码风就知道这也不是我写的

#include
#include
using namespace std;
int n,m,tree[4000001];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        scanf("%d",&tree[n+i]);
    tree[2*n+1]=2e9;
    for(int i=n;i;--i)
        tree[i]=min(tree[i<<1],tree[i<<1|1]);//建树
    puts("0");
    for(int i=2;i<=n;++i)
    {
        int l=max(n,i-m-1+n),r=i+n;//zkw适合开区间查询
        int ans=2e9;
        while(l^r^1)//zkw线段树的查询
        {
            if(~l&1) ans=min(tree[l^1],ans);
            if(r&1) ans=min(tree[r^1],ans);
            l>>=1,r>>=1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

The end

 

转载于:https://www.cnblogs.com/hkttg/p/8876486.html

你可能感兴趣的:([luogu] P1440 求m区间内的最小值)