洛谷传送门
BZOJ传送门
题目描述
你有一个 N×N N × N 的棋盘,每个格子内有一个整数,初始时的时候全部为 0 0 ,现在需要维护两种操作:
1 x y A
, A A 是正整数。将格子x
,y
里的数字加上2 x1 y1 x2 y2
1≤x1≤x2≤N 1 ≤ x 1 ≤ x 2 ≤ N , 1≤y1≤y2≤N 1 ≤ y 1 ≤ y 2 ≤ N 。输出 x1,y1,x2,y2 x 1 , y 1 , x 2 , y 2 这个矩形内的数字和3
无 终止程序输入输出格式
输入格式:
输入文件第一行一个正整数 N N 。
接下来每行一个操作。每条命令除第一个数字之外,均要异或上一次输出的答案
last_ans
,初始时last_ans
=。输出格式:
对于每个 2 2 操作,输出一个对应的答案。
输入输出样例
输入样例#1:
4 1 2 3 3 2 1 1 3 3 1 1 1 1 2 1 1 0 7 3
输出样例#1:
3 5
说明
,操作数不超过 200000 200000 个,内存限制 20M 20 M ,保证答案在 int i n t 范围内并且解码之后数据仍合法。
解题分析
本来可以用 CDQ C D Q 或者树套树水过去的, 然而又卡内存又强制在线…只好 KD−Tree K D − T r e e 了
因为支持插入操作, 和 BST B S T 一样插入后可能退化为链, 于是我们模仿替罪羊树的写法部分重构就好了。
操作不超过 200000 200000 , 所以开 200000 200000 的数组即可。
因为要部分重构, 所以需要开内存池。
细节见下:
#include <cstdio> #include <cmath> #include <cstring> #include <cctype> #include <cstdlib> #include <algorithm> #define R register #define IN inline #define gc getchar() #define W while #define MX 200050 #define alpha 0.75 template <class T> IN void in(T &x) { x = 0; R char c = gc; for (; !isdigit(c); c = gc); for (; isdigit(c); c = gc) x = (x << 1) + (x << 3) + c - 48; } int dot, q, dim, top, root, mnx, mxx, mny, mxy; struct Point {int cood[2], val;} pt[MX]; struct Node {int son[2], lim[2][2], siz, sum; Point info;} tree[MX]; IN bool operator < (const Point &x, const Point &y) {return x.cood[dim] == y.cood[dim] ? x.cood[dim ^ 1] < y.cood[dim ^ 1] : x.cood[dim] < y.cood[dim];} int pool[MX]; namespace KDT { #define ls tree[now].son[0] #define rs tree[now].son[1] IN int nw() {return pool[top--];} IN void pushup(R int now) { tree[now].sum = tree[now].info.val; tree[now].siz = 1; tree[now].lim[0][0] = tree[now].lim[0][1] = tree[now].info.cood[0]; tree[now].lim[1][0] = tree[now].lim[1][1] = tree[now].info.cood[1]; if(ls) { tree[now].lim[0][0] = std::min(tree[now].lim[0][0], tree[ls].lim[0][0]); tree[now].lim[0][1] = std::max(tree[now].lim[0][1], tree[ls].lim[0][1]); tree[now].lim[1][0] = std::min(tree[now].lim[1][0], tree[ls].lim[1][0]); tree[now].lim[1][1] = std::max(tree[now].lim[1][1], tree[ls].lim[1][1]); tree[now].sum += tree[ls].sum; tree[now].siz += tree[ls].siz; } if(rs) { tree[now].lim[0][0] = std::min(tree[now].lim[0][0], tree[rs].lim[0][0]); tree[now].lim[0][1] = std::max(tree[now].lim[0][1], tree[rs].lim[0][1]); tree[now].lim[1][0] = std::min(tree[now].lim[1][0], tree[rs].lim[1][0]); tree[now].lim[1][1] = std::max(tree[now].lim[1][1], tree[rs].lim[1][1]); tree[now].sum += tree[rs].sum; tree[now].siz += tree[rs].siz; } } int build(R int lef, R int rig, R int dm)//重建 { if(lef > rig) return 0; dim = dm; int mid = lef + rig >> 1; int now = nw(); std::nth_element(pt + lef, pt + mid, pt + rig + 1); tree[now].info = pt[mid]; ls = build(lef, mid - 1, dm ^ 1); rs = build(mid + 1, rig, dm ^ 1); pushup(now); return now; } void crash(R int now, R int ord)//将节点压为序列, 保存点的信息 { if(ls) crash(ls, ord); pt[tree[ls].siz + ord + 1] = tree[now].info, pool[++top] = now;//回收节点 if(rs) crash(rs, ord + 1 + tree[ls].siz); } IN void isok(int &now, R int dm)//判定是否需要压扁 {//因为涉及重构, 要传引用 if(tree[ls].siz > tree[now].siz * alpha || tree[rs].siz > tree[now].siz * alpha) crash(now, 0), now = build(1, tree[now].siz, dm); } void insert(int &now, const Point &tar, R int dm) { if(!now) { now = nw(); tree[now].info = tar; ls = rs = 0; pushup(now); return; } dim = dm; if(tar.cood[dm] <= tree[now].info.cood[dm]) insert(ls, tar, dm ^ 1); else insert(rs, tar, dm ^ 1); pushup(now), isok(now, dm); } IN bool init(R int xd, R int xu, R int yd, R int yu)//完全在内部 {return (xd >= mnx && xu <= mxx && yd >= mny && yu <= mxy);} IN bool outit(R int xd, R int xu, R int yd, R int yu)//完全在外部 {return (xd > mxx || xu < mnx || yd > mxy || yu < mny);} int query(R int now) { if(!now) return 0; int ret = 0; if (init(tree[now].lim[0][0], tree[now].lim[0][1], tree[now].lim[1][0], tree[now].lim[1][1])) return tree[now].sum; if (outit(tree[now].lim[0][0], tree[now].lim[0][1], tree[now].lim[1][0], tree[now].lim[1][1])) return 0; if (init(tree[now].info.cood[0], tree[now].info.cood[0], tree[now].info.cood[1], tree[now].info.cood[1])) ret += tree[now].info.val; return ret + query(ls) + query(rs); } #undef ls #undef rs } int main(void) { int typ, a, b, c, last = 0; Point cur; in(dot); int rock_du_orz = 666; for (R int i = 1; i <= 200000; ++i) pool[i] = i; top = 200000; W (rock_du_orz) { in(typ); if(typ == 1) in(a), in(b), in(c), cur.cood[0] = a ^ last, cur.cood[1] = b ^ last, cur.val = c ^ last, KDT::insert(root, cur, 0); else if(typ == 2) { in(mnx), in(mny), in(mxx), in(mxy); mnx ^= last, mny ^= last, mxx ^= last, mxy ^= last; if(mnx > mxx) std::swap(mnx, mxx); if(mny > mxy) std::swap(mny, mxy); printf("%d\n", last = KDT::query(root)); } else break; } }