士兵杀敌(三)
时间限制:
2000 ms | 内存限制:
65535 KB
难度:
5
-
描述
-
南将军统率着N个士兵,士兵分别编号为1~N,南将军经常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比较,计算出两个人的杀敌数差值,用这种方法一方面能鼓舞杀敌数高的人,另一方面也算是批评杀敌数低的人,起到了很好的效果。
所以,南将军经常问军师小工第i号士兵到第j号士兵中,杀敌数最高的人与杀敌数最低的人之间军功差值是多少。
现在,请你写一个程序,帮小工回答南将军每次的询问吧。
注意,南将军可能询问很多次。
-
输入
-
只有一组测试数据
第一行是两个整数N,Q,其中N表示士兵的总数。Q表示南将军询问的次数。(1<N<=100000,1<Q<=1000000)
随后的一行有N个整数Vi(0<=Vi<100000000),分别表示每个人的杀敌数。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。
输出
- 对于每次询问,输出第m号士兵到第n号士兵之间所有士兵杀敌数的最大值与最小值的差。 样例输入
-
5 2 1 2 6 9 3 1 2 2 4
样例输出
-
1 7
-------------------------------------------------RMQ范围最大最小值--------------------------------------------------------
//RMQ:令d(i,j)表示从i开始的,长度为2 ^ j的一段元素中的最小值; //递推公式为:d(i,j) = min{d(i,j - 1),d(i + 2 ^ (j - 1),j - 1)} #include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<algorithm> using namespace std; int a[100005]; int d[100005][20];//最小值 int dp[100005][20];//最大值 int n,m; int from,to; void RMQ_init() { for(int i = 0;i < n;i++)//初始化 { d[i][0] = a[i]; dp[i][0] = a[i]; } for(int j = 1;(1 << j) <= n;j++) { for(int i = 0;i + (1 << j) - 1 < n;i++) { d[i][j] = min(d[i][j - 1],d[i + (1 << (j - 1))][j - 1]); dp[i][j] = max(dp[i][j - 1],dp[i + (1 << (j - 1))][j - 1]); } } } int RMQ(int L,int R) // 查询 { int k = 0; while((1 << (k + 1)) <= (R - L + 1)) //如果2^(k + 1) <= R - L + 1,那么k还可以+1 k++; int minv = min(d[L][k],d[R - (1 << k) + 1][k]); int maxv = max(dp[L][k],dp[R - (1 << k) + 1][k]); return maxv - minv; } int main() { scanf("%d %d",&n,&m); for(int i = 0;i < n;i++) scanf("%d",&a[i]); RMQ_init(); for(int i = 0;i < m;i++) { scanf("%d %d",&from,&to); printf("%d\n",RMQ(from - 1,to - 1)); } return 0; }
用线段树也可以解决// ★★★★★★★ 线段树 : 单点修改 查询范围最值 ★★★★★★★ #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <algorithm> #define INF 0x3f3f3f3f #define Lson o * 2,L,M // 左子树部分 #define Rson o * 2 + 1,M + 1,R // 右子树部分 #define MAX_N 200000 * 3 + 10 using namespace std; int Minv[MAX_N],Maxv[MAX_N]; void Build(int o,int L,int R) // o 当前节点的值,L 区间的左边界,R 区间的右边界 { if(L == R) // 区间长度与 1 ,为叶节点 { scanf("%d",&Minv[o]); // 输入叶子节点 Maxv[o] = Minv[o]; } else { int M = (R + L) / 2; Build(Lson); // 区间长度不为 1 ,不是叶节点 ,建 左子树 Build(Rson); // 区间长度不为 1 ,不是叶节点 ,建 右子树 Minv[o] = min(Minv[o * 2],Minv[o * 2 + 1]); // 取左右孩子的最小值 Maxv[o] = max(Maxv[o * 2],Maxv[o * 2 + 1]); } } int ql,qr; // 查询范围[ql,qr] 中的最小值 int Query(int o,int L,int R) //o 当前节点的值,L 区间的左边界,R 区间的右边界 { int M = (R + L) / 2,ans = INF; if(ql <= L && R <= qr) return Minv[o]; // 当前节点完全包含在查询区间内 if(ql <= M) ans = min(ans,Query(Lson)); // 往左走 if(M < qr) ans = min(ans,Query(Rson)); // 往右走 return ans; } int Max_Query(int o,int L,int R) //o 当前节点的值,L 区间的左边界,R 区间的右边界 { int M = (R + L) / 2,ans = -1; if(ql <= L && R <= qr) return Maxv[o]; // 当前节点完全包含在查询区间内 if(ql <= M) ans = max(ans,Max_Query(Lson)); // 往左走 if(M < qr) ans = max(ans,Max_Query(Rson)); // 往右走 return ans; } int main() { int n,m; while(cin >> n >> m) { Build(1,1,n); for(int i = 0;i < m;i++) { scanf("%d%d",&ql,&qr); // 要查询的范围 [ql,qr] int ans = Max_Query(1,1,n) - Query(1,1,n); // 查询操作 cout << ans << endl; } } return 0; } // ★★★★★★★ 线段树 : 单点修改 查询范围最值 结束 ★★★★★
-
只有一组测试数据