NYOJ 士兵杀敌(三)(线段树,树状数组)

本文介绍了一种高效的算法来解决频繁查询特定区间内最大值与最小值的问题,该问题源于一个军事背景的故事,其中涉及到了线段树和树状数组两种数据结构的设计与实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

士兵杀敌(三)

时间限制: 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

//线段树
#include<stdio.h>
#define M 300005
int max[M],min[M];
int a,b,maxs,mins;
void CreateTree(int L,int R,int root)
{
	if(L==R)
	{
		scanf("%d",&max[root]);
		min[root]=max[root];
		return;
	}
	int mid=(L+R)/2;
	CreateTree(L,mid,root*2);
	CreateTree(mid+1,R,root*2+1);
	max[root]=max[root*2]>max[root*2+1]?max[root*2]:max[root*2+1];
	min[root]=min[root*2]<min[root*2+1]?min[root*2]:min[root*2+1];
}
void Query(int L,int R,int root)
{
	if(a>R||b<L)
		return;
	if(a<=L&&b>=R)
	{
		if(maxs<max[root])
			maxs=max[root];
		if(mins>min[root])
			mins=min[root];
		return;
	}
	int mid=(L+R)/2;
	Query(L,mid,root*2);
	Query(mid+1,R,root*2+1);
}
int main()
{
	int N,Q;
	scanf("%d%d",&N,&Q);
	CreateTree(1,N,1);
	while(Q--)
	{
		scanf("%d%d",&a,&b);
		maxs=0;
		mins=100000000;
		Query(1,N,1);
		printf("%d\n",maxs-mins);
	}
	return 0;
}

//树状数组
#include <iostream>
#include <cstdio>
#include <string.h>
#include <vector>
using namespace std;

const int M = 100005;
int num[M] = {}, MAX[M], MIN[M];

int lowbit(const int x) 
{ 
	return x&-x; 
}
void update(const int id, const int n) 
{
	int i = id;
	while (i <= n) 
	{
		if (MAX[i] == 0 || num[id] > num[ MAX[i] ] || num[id] == num[ MAX[i] ] && id > MAX[i])
		MAX[i] = id;
		i += lowbit(i);
	}
	i = id;
	while (i <= n) 
	{
		if (MIN[i] == 0 || num[id] < num[ MIN[i] ] || num[id] == num[ MIN[i] ] && id > MIN[i])
		MIN[i] = id;
		i += lowbit(i);
	}
}
int get_max(const int s, const int t) 
{
	int i = t, h = num[t];
	while (i >= s) 
	{
		if (MAX[i] >= s) 
		{
			h = max(num[ MAX[i] ], h);
			if (lowbit(i) == i) 
			break;
			i -= lowbit(i);
		} 
		else
		{
			h = max(h, num[i]);
			--i;
		}
	}
	return h;
}
int get_min(const int s, const int t) 
{
	int i = t, l = num[t];
	while (i >= s) 
	{
		if (MIN[i] >= s) 
		{
			l = min(num[ MIN[i] ], l);
			if (lowbit(i) == i) break;
			i -= lowbit(i);
		} 
		else 
		{
			l = min(l, num[i]);
			--i;
		}
	}
	return l;
}
int main() 
{
	int n, q;
	while (cin >> n >> q) 
	{
		memset(MAX, 0, sizeof(MAX));
		memset(MIN, 0, sizeof(MIN));
		for (size_t i = 1; i != n + 1; ++i) 
		{
			scanf("%d", &num[i]);
			update(i, n);
		}
		while (q--) 
		{
			int s, t;
			scanf("%d%d", &s, &t);
			printf("%d\n", get_max(s, t) - get_min(s, t));
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值