我越來越覺得之前自己的代碼寫的就跟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;
}