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.

输入

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;
}

  

 

转载于:https://www.cnblogs.com/agenthtb/p/8978608.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值