【27.77%】【BZOJ 4066】简单题

Time Limit: 50 Sec  Memory Limit: 20 MB
Submit: 1919  Solved: 533
[Submit][Status][Discuss]

Description

你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:

 

命令

参数限制

内容

1 x y A

1<=x,y<=N,A是正整数

将格子x,y里的数字加上A

2 x1 y1 x2 y2

1<=x1<= x2<=N

1<=y1<= y2<=N

输出x1 y1 x2 y2这个矩形内的数字和

3

终止程序

Input

输入文件第一行一个正整数N。
接下来每行一个操作。每条命令除第一个数字之外,
均要异或上一次输出的答案last_ans,初始时last_ans=0。

Output

对于每个2操作,输出一个对应的答案。

Sample Input

4
1 2 3 3
2 1 1 3 3
1 1 1 1
2 1 1 0 7
3

Sample Output

3
5

HINT

数据规模和约定

1<=N<=500000,操作数不超过200000个,内存限制20M,保证答案在int范围内并且解码之后数据仍合法。

样例解释见OJ2683


新加数据一组,但未重测----2015.05.24


Source


【题解】

在做kd-tree的时候会记录这个子树的最大矩形范围。如果整个最大矩形范围都在所求的矩形范围内。就直接返回这个最大矩形所在的子树的和。

如果不满足的话就看看当前节点是否在所求矩形内。

如果在就先累加这个节点的。

再递归求解左子树和右子树;

然后加的那组数据会卡时.

要不定时重建整棵树。(10000为周期)

【代码】

#include <cstdio>
#include <algorithm>

const int MAXN = 209000;

using namespace std;

struct point
{
	int d[2], ma_x[2], mi_n[2], l, r, sum, v;
};

int n, la = 0, v, root = 0, totn, a1, b1, a2, b2, now;
point t[MAXN], op;

void up_data(int rt)
{
	int l = t[rt].l, r = t[rt].r;
	for (int i = 0; i <= 1; i++)
	{
		if (l)
		{
			t[rt].ma_x[i] = max(t[l].ma_x[i], t[rt].ma_x[i]);
			t[rt].mi_n[i] = min(t[l].mi_n[i], t[rt].mi_n[i]);
		}
		if (r)
		{
			t[rt].ma_x[i] = max(t[r].ma_x[i], t[rt].ma_x[i]);
			t[rt].mi_n[i] = min(t[r].mi_n[i], t[rt].mi_n[i]);
		}
	}
	t[rt].sum = t[l].sum + t[r].sum + t[rt].v;//重建后要更新sum值。
}

void insert(int &rt, int fx)
{
	if (rt == 0) //创节点。更新信息
	{
		rt = ++totn;
		t[rt] = op;
		t[rt].l = t[rt].r = 0;
		for (int i = 0; i <= 1; i++)
			t[rt].ma_x[i] = t[rt].mi_n[i] = t[rt].d[i];
		t[rt].sum = v;
		t[rt].v = v;
		return;
	}
	else
	{
		if (op.d[fx] <= t[rt].d[fx])
			insert(t[rt].l, 1 - fx);
		else
			insert(t[rt].r, 1 - fx);
	}
	up_data(rt);
}

int query(int rt, int fx)
{
	if (!rt) return 0;
	if (op.mi_n[0] <= t[rt].mi_n[0] && t[rt].ma_x[0] <= op.ma_x[0] &&
		op.mi_n[1] <= t[rt].mi_n[1] && t[rt].ma_x[1] <= op.ma_x[1])
		return t[rt].sum; //整个子树都在范围内
	int temp = 0, l = t[rt].l, r = t[rt].r;
	if (op.mi_n[0] <= t[rt].d[0] && t[rt].d[0] <= op.ma_x[0] &&
		op.mi_n[1] <= t[rt].d[1] && t[rt].d[1] <= op.ma_x[1]) //这个点在范围内
		temp += t[rt].sum - t[l].sum - t[r].sum; //减去两个子树的就是当前这个点的
	//或直接加t[rt].v
	if (op.mi_n[fx] <= t[rt].d[fx]) 
		temp += query(l, 1 - fx);
	if (t[rt].d[fx] <= op.ma_x[fx])
		temp += query(r, 1 - fx);
	return temp;
}

bool cmp(point a, point b)
{
	if (a.d[now] < b.d[now])
		return true;
	return false;
}

int build(int begin, int end, int fx)
{
	int m = (begin + end) >> 1;
	now = fx;
	nth_element(t + begin, t + m, t + end + 1, cmp);
	for (int i = 0; i <= 1; i++)
		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
	t[m].sum = t[m].v;
	if (begin < m)
		t[m].l = build(begin, m - 1, 1 - fx);
	else
		t[m].l = 0;
	if (m < end)
		t[m].r = build(m + 1, end, 1 - fx);
	else
		t[m].r = 0;
	up_data(m);
	return m;
}

void input_data()
{
	scanf("%d", &n);
	while (true)
	{
		int cz;
		scanf("%d", &cz);
		if (cz == 1)
		{
			if ((totn != 0) && (totn % 10000) == 0) //重建树
				root = build(1, totn, 0);
			scanf("%d%d%d", &op.d[0], &op.d[1], &v);
			op.d[0] ^= la; op.d[1] ^= la; v ^= la;
			insert(root, 0);
		}
		else
			if (cz == 2)
			{
				scanf("%d%d%d%d", &op.mi_n[0], &op.mi_n[1], &op.ma_x[0], &op.ma_x[1]);
				op.mi_n[0] ^= la; op.mi_n[1] ^= la;
				op.ma_x[0] ^= la; op.ma_x[1] ^= la;
				la = query(root, 0);
				printf("%d\n", la);
			}
			else
				break;
	}
}

int main()
{
	//freopen("F:\\rush.txt", "r", stdin);
	input_data();
	return 0;
}


posted @ 2017-10-06 19:23  AWCXV  阅读(91)  评论(0编辑  收藏  举报