luogu3801 红色的幻想乡

Posted headboy2002

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu3801 红色的幻想乡相关的知识,希望对你有一定的参考价值。

题目大意

  给一个初始值都是0的0-1矩阵,两个操作:1.选择一个点,将其所在排和列(不包括该点)的数字取反。2.求一个子矩形内的数字和。n,m,q<=100000.

错误思路

为何不能用二维线段树

  1. n,m<=100000,每个x线段树都维护一个有400000个节点的Y线段树,而X节点也要400000个,空间受不了。
  2. 如果我们要更新一排,X线段树没有达到“排除一半”的功能,必须遍历到所有X、Y节点,时间受不了。

假命题:将所在排列其它数字取反,等价于把选择的点的数字取反

  与后者等价的,是把整个矩阵的其它数字都取反,而不仅仅是其所在排列的。

正确题解

  不要被“不包括该点”迷惑。翻转不包括该点,相当于先翻转该点所在排,再翻转所在列,该点翻转了两次,数字仍然不变。

  这样,我们就可以从对排和对列单独分析作为思路了。我们可以对于各排和各列定义一个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
", xCnt * (y2 - y1 + 1) + yCnt * (x2 - x1 + 1) - xCnt * yCnt * 2);
            break;
        }
    }
    return 0;
}

  

 

以上是关于luogu3801 红色的幻想乡的主要内容,如果未能解决你的问题,请参考以下文章

AC日记——红色的幻想乡 洛谷 P3801

洛谷p3801:红色的幻想乡

P3801 红色的幻想乡

LG3801 红色的幻想乡 线段树+容斥原理

luogu P3345 [ZJOI2015]幻想乡战略游戏(点分树)

红色的幻想乡