[luogu] P1440 求m区间内的最小值
题目描述
一个含有n项的数列(n<=2000000),求出每一项前的m个数到它这个区间内的最小值。若前面的数不足m项则从第1个数开始,若前面没有数则输出0。
输入输出格式
输入格式:
第一行两个数n,m。
第二行,n个正整数,为所给定的数列。
输出格式:
n行,第i行的一个数ai,为所求序列中第i个数前m个数的最小值。
输入输出样例
说明
【数据规模】
m≤n≤2000000
很显而易见的板子题。想练练RMQ-ST
但是MAXN = 2000000,用没有滚动数组RMQ-ST会MLE两个点。。。
1 #include <cstdio> 2 #include <algorithm> 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<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<iostream> 5 #include<algorithm> 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 <bits/stdc++.h> 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<cstdio> #include<algorithm> 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