标签:private 表示 分析 def turn print 其它 include mes
给一个初始值都是0的0-1矩阵,两个操作:1.选择一个点,将其所在排和列(不包括该点)的数字取反。2.求一个子矩形内的数字和。n,m,q<=100000.
与后者等价的,是把整个矩阵的其它数字都取反,而不仅仅是其所在排列的。
不要被“不包括该点”迷惑。翻转不包括该点,相当于先翻转该点所在排,再翻转所在列,该点翻转了两次,数字仍然不变。
这样,我们就可以从对排和对列单独分析作为思路了。我们可以对于各排和各列定义一个0-1状态表示如果不存在交叉点,则该排上的所有数字是1还是0。定义一排(列)的状态为1,且经过一个矩形,则该排(列)穿过该矩形。这样,我们要查询的子矩形内的数字和就等于穿过该矩形的排数*该矩形占的列数+穿过该矩形的列数*该矩形占的排数-交叉点个数*2(其=穿过该矩形的排数*穿过该矩形的列数)。穿过矩形的排数、列数可以分别对排、列维护一个单点修改,区间查询的线段树表示区间[l,r]内状态为1的点的个数是多少。
#include <cstdio> #include <cstring> #include <algorithm> #include <cassert> using namespace std; #define ll long long const int MAX_NODE = 100010 * 4; struct RangeTree { private: int Sum[MAX_NODE]; int N; void Update(int cur, int l, int r, int p) { if(l == r) { assert(p == r && p == l); assert(Sum[cur] < 2); Sum[cur] = !Sum[cur]; return; } int mid = (l + r) / 2; if(p <= mid) Update(cur * 2, l, mid, p); if(p > mid) Update(cur * 2 + 1, mid + 1, r, p); Sum[cur] = Sum[cur * 2] + Sum[cur * 2 + 1]; } ll Query(int cur, int sl, int sr, int al, int ar) { if(al <= sl && sr <= ar) return Sum[cur]; ll ans = 0; int mid = (sl + sr) / 2; if(al <= mid) ans += Query(cur * 2, sl, mid, al, ar); if(ar > mid) ans += Query(cur * 2 + 1, mid + 1, sr, al, ar); return ans; } public: RangeTree(int n):N(n){} void Update(int p) { Update(1, 1, N, p); } ll Query(int l, int r) { return Query(1, 1, N, l, r); } }; int main() { int n, m, opCnt; scanf("%d%d%d", &n, &m, &opCnt); static RangeTree X(n), Y(m); while(opCnt--) { int op, x1, x2, y1, y2; ll xCnt, yCnt, totX, totY; scanf("%d", &op); switch(op) { case 1: scanf("%d%d", &x1, &y1); X.Update(x1); Y.Update(y1); break; case 2: scanf("%d%d%d%d", &x1, &y1, &x2, &y2); xCnt = X.Query(x1, x2); yCnt = Y.Query(y1, y2); //totX = X.Query(1, n); //totY = Y.Query(1, m); printf("%lld\n", xCnt * (y2 - y1 + 1) + yCnt * (x2 - x1 + 1) - xCnt * yCnt * 2); break; } } return 0; }
标签:private 表示 分析 def turn print 其它 include mes
原文地址:https://www.cnblogs.com/headboy2002/p/9206375.html