南将军统率着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号士兵。
5 2 1 2 6 9 3 1 2 2 4
1 7
NYOJ
由于oj的原因,这道题用线段树过不了,估计是卡时间,线段树T了十几发,最后一发RMQ就过了,不得不得说RMQ对于解决数组长度短,查询次数多的区间最值问题比线段树快多了;
RMQ的需要预处理,时间是O(nlogn),查询时间是O(1);
RMQ运用了动态规划的思想,对长度为n的数列A,进行区间最值查询;
设A[i]是要求区间最值的数列,
maxnum[i][j]表示从第i个数起连续的2^j个数中的最大值;
比如A数组为7 8 9 5 6 7 4 2 9
即maxnum[1][0] 表示从第1个数起,2^0 = 1 个数的最大值,即为:maxnum[1][0] = 7
所以 maxnum[1][1] = 8;maxnum[1][3] = 9;maxnum[5][1] = 7;.......
任意的maxnum[i][j] 都可以分成两段,即为maxnum[i][j-1]和maxnum[i+(1<<(j-1))][j-1];
所以动态转移方程为:maxnum[i][j] = max(maxnum[i][j-1] , maxnum[i+(1<<(j-1))][j-1]);
查询功能,比如查询(Al,Ar)之间的最值问题,只需要找到满足2^k <= (r-l+1)的k的最大值;
比如l = 2,r = 5;那么k = 2;l = 1; r = 5 那么k = 2;这个k的作用,其实就是一个计数的作用,即我们只需要求l后面2^k个数的最值和r前面2^k个数的最值即可。
k = (int) (log(r-l+1.0)/log(2.0));
所以MAX = max(maxnum[l][k],maxnum[r-(1<<k)+1][k])
具体代码如下:
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define eps 1e-10
#define maxn 300005
#define zero(a) fabs(a)<eps
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson step<<1
#define rson step<<1|1
#define MOD 1000000009
#define sqr(a) ((a)*(a))
using namespace std;
int maxnum[100005][22];//最大值储存
int minnum[100005][22];//最小值储存
void RMQ(int n) {
for(int j = 1; j < 20; j++) {//因为2^20大于100005 足够了
for(int i = 1; i <= n; i++) {
if(i+(1<<j)-1 <= n) {
maxnum[i][j] = Max(maxnum[i][j-1],maxnum[i+(1<<(j-1))][j-1]);
minnum[i][j] = Min(minnum[i][j-1],minnum[i+(1<<(j-1))][j-1]);
}
}
}
}
int main() {
int N , Q;
int m , n;
scanf("%d%d",&N,&Q);
for(int i = 1; i <= N; i++) {
scanf("%d",&maxnum[i][0]);// 赋初始值
minnum[i][0] = maxnum[i][0];
}
RMQ(N);//RMQ初始化
while(Q--) {
scanf("%d%d",&m,&n);
int k = (int)(log(n-m+1.0)/log(2.0));
int mx = Max(maxnum[m][k],maxnum[n-(1<<k)+1][k]);
int mn = Min(minnum[m][k],minnum[n-(1<<k)+1][k]);
printf("%d\n",mx - mn);
}
return 0;
}
线段树代码:
#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define eps 1e-10
#define maxn 400005
#define zero(a) fabs(a)<eps
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson step<<1
#define rson step<<1|1
#define MOD 1000000009
#define sqr(a) ((a)*(a))
using namespace std;
int v[maxn];
int mx,mn;
struct node {
int left;
int right;
int valuemax;
int valuemin;
}node[maxn];
void Build(int i , int l , int r) {
node[i].left = l;
node[i].right = r;
if(l == r)
{
node[i].valuemax = v[l];
node[i].valuemin = v[l];
return ;
}
Build(i<<1 , l , (r+l)/2);
Build((i<<1)+1 , 1+(r+l)/2 , r);
node[i].valuemax = Max(node[i<<1].valuemax , node[(i<<1)+1].valuemax);
node[i].valuemin = Min(node[i<<1].valuemin , node[(i<<1)+1].valuemin);
return ;
}
void Query(int i, int l, int r) {
if(node[i].left == l && node[i].right == r) {
mx = Max(mx , node[i].valuemax);
mn = Min(mn , node[i].valuemin);
return ;
}
i = i<<1; //左子节点
if(l <= node[i].right) {
if(r <= node[i].right)
Query(i , l , r);
else
Query(i , l , node[i].right);
}
i++; //右子节点
if(r >= node[i].left) {
if(l >= node[i].left)
Query(i , l , r);
else
Query(i , node[i].left , r);
}
return ;
}
int main() {
int N , M;
int m , n;
scanf("%d%d",&N,&M);
for(int i = 1; i <= N; i++) {
scanf("%d",&v[i]);
}
Build(1, 1, N);
while(M--) {
scanf("%d%d",&m,&n);
mn = inf;
mx = -inf;
Query(1, m, n);
printf("%d\n",mx-mn);
}
return 0;
}