nyoj 1215 - 士兵杀敌(三)

题目描述

南将军统率着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
提示
来源

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值