树状数组相关应用之区间包含问题

区间包含问题

对于一维区间问题一般是采用定一议二的方法
区间外包含问题:POJ-2481
在这里插入图片描述
在这里插入图片描述
对于此问题我们可以先按x升序排序(x值相同,y大的排在前面),保证之后输入的区间的左端必在之前输入区间之内,若x值相等,y降序,则之后输入区间的右端必在之前输入区间之内,那么此题便转化为对y求后缀数组和问题

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <algorithm>

#define MAXSIZE 100005

using namespace std;   //使用名称空间std

struct cow
{
	int no;     //编号
	int S, E;   //区间
	int num;    //比其强壮奶牛数量
};

cow arry[MAXSIZE];
int val[MAXSIZE];

int main()
{
	bool cmp(cow a, cow b);
	bool cmp_no(cow a, cow b);
	int lowbit(int x);
	void update_1(int val[], int i, int cal, int arry_num);
	int sum_pre_1(int val[], int i);
	int sum_between_1(int val[], int pre, int last);

	int flag;
	int begin=0, end=0;     //记录区间最大范围,在此范围内构建树状数组
	int pre, last;      //记录单个区间外的前方和后方奶牛数
	while (~scanf("%d",&flag))
	{
		if (!flag)
			break;
		memset(val, 0, sizeof(val));
		begin = MAXSIZE;
		end = 0;
		for (int i = 1;i <= flag;i++)
		{
			arry[i].no = i;
			scanf("%d %d", &arry[i].S, &arry[i].E);
			begin = begin < arry[i].S ? begin : arry[i].S;
			end = end > arry[i].E ? end : arry[i].E;
		}
		begin++;            //区间整体后移(避免区间左端为0的情况)
		end++;
		//数据输入完成
		sort(arry + 1, arry + flag + 1, cmp);       //先将左端小的排在前,如果左端相等,再将右端大的排在前
		//原数组处理完毕
		int temp = 0;       //暂存器
		for (int i = 1;i <= flag;i++)
		{
			if (arry[i].S==arry[i - 1].S&&arry[i].E == arry[i - 1].E&&i != 1)
				arry[i].num = temp;
			else
			{
				arry[i].num = sum_between_1(val, arry[i].E + 1, end);
				temp = arry[i].num;
			}

			update_1(val, arry[i].S + 1, 1, end);
			update_1(val, arry[i].E + 1, 1, end);
		}
		sort(arry + 1, arry + flag + 1, cmp_no);   //数组顺序复原
		printf("%d", arry[1].num);
		for (int i = 2;i <= flag;i++)
			printf(" %d", arry[i].num);
		printf("\n");
	}
	return 0;
}

bool cmp(cow a, cow b)
{//先将左端小的放在前,若左端相等,则将右端大的放在前
	if (a.S != b.S)
		return a.S < b.S;
	else
		return a.E > b.E;
}

bool cmp_no(cow a, cow b)
{
	return a.no < b.no;
}

int lowbit(int x)
{//返回二进制数最低位的1对应的数值
	return x & (-x);      //与运算
}

//一维树状数组
void update_1(int val[], int i, int cal, int arry_num)
{//原数组第i个元素加上cal,更新树状数组相关元素,arry_num为原数组的长度
 //可直接用于树状数组的建立
	for (;i <= arry_num;i += lowbit(i))
		val[i] += cal;
}

int sum_pre_1(int val[], int i)
{//求arry数组的前i项和
 //val为树状数组地址
	int sum = 0;
	for (;i > 0;i -= lowbit(i))       //从后向前每次跳一个lowbit
		sum += val[i];
	return sum;
}

int sum_between_1(int val[], int pre, int last)
{//求原数组arry在区间[pre-last]的和
	return sum_pre_1(val, last) - sum_pre_1(val, pre - 1);
}

区间内包含问题则按x降序,y升序排列,对y求前缀数组和即可

猜你喜欢

转载自blog.csdn.net/qq_40432881/article/details/83145086