HDU 4819:单点更新,区间查询的二维线段树

我越来越觉得之前自己的代码写的就跟shi一样……


题意很简单,矩阵中指定一个方形,查询出这个方形中的最大值和最小值,然后把矩阵中间那个点改成它们的平均数。这个就是很明显的二维线段树了……


这种改单点,问区间的二维线段树在建立和修改的时候有个问题:


00000
00000
00000
00600
00000

以纵向为X轴,横向为Y轴建立二维线段树,考虑一下,当我把(4,3)这个点改成6的时候,我肯定是要深入到X=4这个节点去改的,然后第4行这个线段树对了;之后我回溯到X=4~5这个节点的时候,就会有点麻烦,你会发现X=4~5的3号位置不好填了,因为X=4的这个节点改了3这个位置以后,并没有通知到他上层的节点。

解决方法是什么呢?我们一定要注意,当Y轴长度为1的时候,X轴方向也是一颗线段树。而且,不同的X节点中,Y轴的那个指针数字相同的,其LR一定是一样的。比如所有的1号Y节点都是表示1~N的。所以,我们在不是叶子节点的X轴节点处深入修改Y节点时,当Y节点已经修改到叶子了,就是当你正要更新(4,3)~(5,3)这个矩形的时候,我们再将纵向看成一棵线段树,去询问X轴方向的相应位置的子树的最值就可以了。这里我们只需要去询问(4,3)(5,3)两个X轴方向的叶子节点,就能完成更新。

由于X轴是自底向上更新的,所以当你询问一个X轴长度为2,Y轴长度为1的矩形的时候,那两个X轴长度为1的肯定已经是对的了;同理,当你问X轴长度为4的矩形的时候,那两个长度为2的子线段也肯定是对的。所以你只需要直接从两个儿子更新一下即可。

在区间查询的时候,比如我查(4,1)~(5,5)这个矩形,到X=4~5处停下就可以了,因为X=4~5里面的信息肯定是对的。


这就是单点修改,区间查询线段树最方便的维护方法。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int MAXN = 810;
const int INF = 1<<30;

struct ntype
{
	int max_value;
	int min_value;
};

ntype node[4*MAXN][4*MAXN];//不定义两种结构体,否则写起来太长,第一维是X轴,第二维是Y轴

int n;
int t;
int q;
int map[1000][1000];

int ask_min_y(int l, int r, int py, int px, int lft, int rght)
{
	if(lft <=l && r<=rght)
	{
		return node[px][py].min_value;
	}
	
	int r1=INF, r2=INF;
	int mid = (l+r)/2;
	if(lft <= mid)
		r1 = ask_min_y(l, mid, py*2, px, lft, rght);
	if(rght >= mid+1)
		r2 = ask_min_y(mid+1, r, py*2+1, px, lft, rght);
	
	return min(r1, r2);
}

int ask_min(int l, int r, int p, int up, int down, int lft, int rght)
{
	if(up<=l && r<=down)
	{
		int r= ask_min_y(1,n,1, p, lft, rght);
		return r;
	}
	
	int r1=INF, r2=INF;
	int mid = (l+r)/2;
	if(up<=mid)
		r1 = ask_min(l, mid, p*2, up, down, lft, rght);
	if(down >= mid+1)
		r2 = ask_min(mid+1, r, p*2+1, up, down, lft, rght);
	
	return min(r1, r2);
}

int ask_max_y(int l, int r, int py, int px, int lft, int rght)
{
	if(lft <=l && r<=rght)
	{
		return node[px][py].max_value;
	}
	
	int r1=-INF, r2=-INF;
	int mid = (l+r)/2;
	if(lft <= mid)
		r1 = ask_max_y(l, mid, py*2, px, lft, rght);
	if(rght >= mid+1)
		r2 = ask_max_y(mid+1, r, py*2+1, px, lft, rght);
	
	return max(r1, r2);
}

int ask_max(int l, int r, int p, int up, int down, int lft, int rght)
//询问最大值,目前范围l~r, 指针=p,上界是up……等等
//这个按照普通的样子写就可以了
{
	if(up<=l && r<=down)
	{
		int ret= ask_max_y(1, n, 1, p, lft, rght);
		return ret;
	}
	
	int r1=-INF, r2=-INF;
	int mid = (l+r)/2;
	if(up<=mid)
		r1 = ask_max(l, mid, p*2, up, down, lft, rght);
	if(down >= mid+1)
		r2 = ask_max(mid+1, r, p*2+1, up, down, lft, rght);
	
	return max(r1, r2);
}

void update_y(int l, int r, int py, int px, bool x_isleaf, int tar_y, int tar_v)
//update_y函数基本原理和build_y函数几乎完全相同
{
	if( l==r && l==tar_y)
	{
		if(x_isleaf)
		{
			node[px][py].max_value = tar_v;
			node[px][py].min_value = tar_v;
		}
		else
		{
			node[px][py].max_value = max(node[px*2][py].max_value, node[px*2+1][py].max_value);
			node[px][py].min_value = min(node[px*2][py].min_value, node[px*2+1][py].min_value);
		}
		return;
	}
	int mid = (l+r)/2;
	if(tar_y <= mid)
		update_y(l, mid, py*2, px, x_isleaf, tar_y, tar_v);
	else
		update_y(mid+1, r, py*2+1, px, x_isleaf, tar_y, tar_v);
	
	node[px][py].max_value = max(node[px][py*2].max_value, node[px][py*2+1].max_value);
	node[px][py].min_value = min(node[px][py*2].min_value, node[px][py*2+1].min_value);
	return;
}

void update(int l, int r, int p, int tar_x, int tar_y, int tar_v)
//目前范围:l~r,指针=p,要修改的点是tar_x, tar_y, 修改值为v
{
	if(l==r && l == tar_x)
	{
		update_y(1, n, 1, p, true, tar_y, tar_v);
		return;
	}
	
	int mid=(l+r)/2;
	if(tar_x <= mid)
		update(l, mid, p*2, tar_x, tar_y, tar_v);
	else
		update(mid+1, r, p*2+1, tar_x, tar_y, tar_v);
	
	update_y(1, n, 1, p, false, tar_y, tar_v);
	return;
}

void init()
{
	memset(node, 0, sizeof(node));
	return;
}

void build_y(int l, int r, int py, int px, bool x_isleaf, int row_num)
//目前范围:l~r,y指针=py,x指针=px
//x_isleaf 代表目前是否是一个X轴的叶子,row_num=列号,只有在x_isleaf=true的时候有意义
{
	if(l==r)
	{
		if(x_isleaf)
		{
			node[px][py].max_value = map[row_num][l];
			node[px][py].min_value = map[row_num][l];
		}
		else //当前的Y轴叶子节点实际上包含一段X区间,去根据这个区间拿到我的值
		{
			node[px][py].max_value = max(node[px*2][py].max_value, node[px*2+1][py].max_value);
			node[px][py].min_value = min(node[px*2][py].min_value, node[px*2+1][py].min_value);
		}
		return;
	}
	
	int mid = (l+r)/2;
	build_y(l, mid, py*2, px, x_isleaf, row_num);
	build_y(mid+1, r, py*2+1, px, x_isleaf, row_num);
	
	node[px][py].max_value = max(node[px][py*2].max_value, node[px][py*2+1].max_value);
	node[px][py].min_value = min(node[px][py*2].min_value, node[px][py*2+1].min_value);
	
	return;
}

void build(int l, int r, int p)//目前范围:l~r, 指针=p
{
	if(l==r)
	{
		build_y(1, n, 1, p, true, l);//X轴建树到叶子节点,建Y
		return;
	}
	int mid = (l+r)/2;
	build(l,mid, p*2);
	build(mid+1, r, p*2+1);
	
	build_y(1, n, 1, p, false, l);//先建好X方向的子树再建Y,才能保证自己Y方向更新对
	return;
}

int main()
{
	scanf("%d", &t);
	int files;
	for(files=1; files<=t; files++)
	{
		init();
		printf("Case #%d:\n", files);
		scanf("%d", &n);
		
		int i,j;
		for(i=1; i<=n;i++)
		{
			for(j=1; j<=n;j++)
			{
				scanf("%d", &map[i][j]);
			}
		}
		
		build(1, n, 1);
		
		scanf("%d", &q);
		for(i=1; i<=q; i++)
		{
			int x, y, l;
			scanf("%d %d %d", &x, &y, &l);
			l--;
			
			int range_l = max(1, y - l/2);
			int range_r = min(n, y + l/2);
			int range_u = max(1, x - l/2);
			int range_d = min(n, x + l/2);
			
			int min1 = ask_min(1, n, 1, range_u, range_d, range_l, range_r);
			int max1 = ask_max(1, n, 1, range_u, range_d, range_l, range_r);
			
			int now = (min1 + max1) / 2;
			printf("%d\n", now);
			update(1, n, 1, x, y, now);
		}
		
	}
	//system("pause");
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值