P3801:红色的幻想乡题解

题意

给定一个平面\((n,m)\),处理如下操作:

  • OPT = 1:使点\((x,y)\)处使横纵行全部0/1取反。
  • OPT = 2:矩形\((x_1,y_1,x_2,y_2)\)内的和。

主要思路

这道题还是很有意思的。

首先设置数组\(\text{stat_x[],stat_y[]}\),记录每一行的0/1情况;再搞两个树状数组,来维护区间内点的个数。在加点的时候,如果当前行/列已被操作过,那就在 stat 数组内取反即可,并且在当前行/列所对应的树状数组中加/减这个点的贡献,也就是\(\pm 1\)。计算的时候,区间查询出长方形长区间和宽区间对应的点数,然后再乘上相应贡献(也就是矩形长度或宽度),然后再减去消掉的点。

代码

// P3801.cpp
#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
using namespace std;
const int MAX_N = 101000;
int n, m, q, x_ctn[MAX_N], y_ctn[MAX_N], x_tree[MAX_N], y_tree[MAX_N], opt, tmpx, tmpy;
void add(int x, int d, int limit, int *tree)
{
    while (x <= limit)
        tree[x] += d, x += lowbit(x);
}
int sum(int x, int *tree)
{
    int ans = 0;
    while (x > 0)
        ans += tree[x], x -= lowbit(x);
    return ans;
}
void change(int x, int y)
{
    x_ctn[x] ^= 1, add(x, x_ctn[x] ? 1 : -1, n, x_tree);
    y_ctn[y] ^= 1, add(y, y_ctn[y] ? 1 : -1, m, y_tree);
}
int main()
{
    scanf("%d%d%d", &n, &m, &q);
    x_tree[0] = n, y_tree[0] = m;
    while (q--)
    {
        scanf("%d", &opt);
        if (opt == 1)
            scanf("%d%d", &tmpx, &tmpy), change(tmpx, tmpy);
        else
        {
            int x1, x2, y1, y2;
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            int s1 = sum(x2, x_tree) - sum(x1 - 1, x_tree);
            int s2 = sum(y2, y_tree) - sum(y1 - 1, y_tree);
            long long ans = 1LL * s1 * (y2 - y1 + 1) + 1LL * s2 * (x2 - x1 + 1) - 2LL * s1 * s2;
            printf("%lld\n", ans);
        }
    }
    return 0;
}

Leave a Reply

Your email address will not be published. Required fields are marked *