HDU 4819:單點更新,區間查詢的二維線段樹

我越來越覺得之前自己的代碼寫的就跟shi一樣……


題意很簡單,矩陣中指定一個方形,查詢出這個方形中的最大值和最小值,然後把矩陣中間那個點改成它們的平均數。這個就是很明顯的二維線段樹了……


這種改單點,問區間的二維線段樹在建立和修改的時候有個問題:


0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 6 0 0
0 0 0 0 0

以縱向爲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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章