2017 ICPC HongKong B:Black and White(扫描线+线段树)
题目描述
Consider a square map with N × N cells. We indicate the coordinate of a cell by (i, j), where 1 ≤ i, j ≤ N . Each cell has a color either white or black. The color of each cell is initialized to white. The map supports the operation
flip([xlow , xhigh], [ylow , yhigh]), which flips the color of each cell in the rectangle [xlow , xhigh] × [ylow , yhigh]. Given
a sequence of flip operations, our problem is to count the number of black cells in the final map. We illustrate this in the following example. Figure (a) shows the initial map. Next, we call flip([2, 4], [1, 3]) and obtain Figure (b). Then, we call flip([1, 5], [3, 5]) and obtain Figure (c). This map contains 18 black cells.
flip([xlow , xhigh], [ylow , yhigh]), which flips the color of each cell in the rectangle [xlow , xhigh] × [ylow , yhigh]. Given
a sequence of flip operations, our problem is to count the number of black cells in the final map. We illustrate this in the following example. Figure (a) shows the initial map. Next, we call flip([2, 4], [1, 3]) and obtain Figure (b). Then, we call flip([1, 5], [3, 5]) and obtain Figure (c). This map contains 18 black cells.
输入
The first line contains the number of test cases T (T < 10). Each test case begins with a line containing two integers N and K (1 < N, K < 10000), where N is the parameter of the map size and K is the number of flip operations. Each subsequent line corresponds to a flip operation, with four integers: xlow , xhigh, ylow , yhigh.
输出
For each test case, output the answer in a line.
样例输入
1
5 2
2 4 1 3
1 5 3 5
样例输出
18
题意:给你一个n*n的白色方块,然后有m次修改,其中每次询问修改一个矩形,这个矩形中的黑块变白,白快变黑,问最后有多少个黑块
思路:扫描线算法+线段树维护
代码如下:
#include <bits/stdc++.h> using namespace std; int read() { char ch = getchar(); int x = 0, f = 1; while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar(); } while('0' <= ch && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } const int maxn = 1e5+500; int n,m,tot=0; int sum[maxn<<2],cnt[maxn<<2],tag[maxn<<2]; struct node { int x,y1,y2; bool operator < (const node &p) const{return x<p.x;} }qu[maxn<<1]; void pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1];//求当前行(线段树)中黑色的块数 cnt[rt]=cnt[rt<<1]+cnt[rt<<1|1];//cnt[i]是第i号节点代表区间内有多少块数 } void pushdown(int rt) { tag[rt<<1]^=tag[rt];//tag标记黑白 tag[rt<<1|1]^=tag[rt]; sum[rt<<1]=cnt[rt<<1]-sum[rt<<1];//当前节点下白变黑,黑变白 sum[rt<<1|1]=cnt[rt<<1|1]-sum[rt<<1|1]; tag[rt]=0; } void build (int l,int r,int rt) { tag[rt]=0; if (l==r){sum[rt]=0,cnt[rt]=1;return;} int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); pushup(rt); } void update(int L,int R,int l,int r,int rt) { if (L<=l&&r<=R){ tag[rt]^=1; sum[rt]=cnt[rt]-sum[rt]; return ; } int mid=(l+r)>>1; if (tag[rt]) pushdown(rt); if (L<=mid) update(L,R,l,mid,rt<<1); if (mid+1<=R) update(L,R,mid+1,r,rt<<1|1); pushup(rt); } int main() { int t=read(); while (t--){ n=read(),m=read(); tot = 0; build(1,n,1); while (m--){ int a=read(),b=read(),c=read(),d=read(); qu[++tot]=node{a,c,d};//将每个操作点拆成两个 if (b+1<=n) qu[++tot]=node{b+1,c,d}; //比如修改2<=x<=4,1<=y<=3的点转化为 //先修改2<=x<=+oo,1<=y<=3 //再修改5<=x<=+oo,1<=y<-3 //跟灯泡开关的原理一样,这样两个拆的操作与原来的操作是等价的 } sort(qu+1,qu+1+tot);//将询问排序 /*for (int i=1;i<=tot;++i){ printf("%d %d %d\n",qu[i].x,qu[i].y1,qu[i].y2); }*/ long long ret = 0; int pos = 1; for (int i=1;i<=n;++i){ while (pos<=tot&&qu[pos].x<=i){//当我们处理第i个询问时 update(qu[pos].y1,qu[pos].y2,1,n,1); //如果还存在没有修改的操作,将它修改 pos++; } //printf("%d\n",sum[i]); ret += sum[1];//如果在当前位置没有修改,那说明这一列与上一列情况一样直接加就行 } printf("%lld\n",ret); } return 0; }