zoukankan      html  css  js  c++  java
  • 【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
    ", la);
    			}
    			else
    				break;
    	}
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	input_data();
    	return 0;
    }


  • 相关阅读:
    【学习】Linux Shell脚本编程
    Linux 系统进程相关命令
    Linux 文件权限管理
    Linux 用户关联命令
    Linux shell 及命令汇总
    服务器通过树莓派控制继电器
    华为绩效管理:这样减员、增效、加薪,不服不行!!
    工作十年的程序员,却拿着毕业三年的工资,再不开窍就真晚了!
    员工离职原因,只有两点最真实,其他都是扯淡!
    最全面的2017物联网安全事件盘点
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632249.html
Copyright © 2011-2022 走看看