码迷,mamicode.com
首页 > 其他好文 > 详细

luogu3801 红色的幻想乡

时间:2018-06-21 00:16:32      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:private   表示   分析   def   turn   print   其它   include   mes   

题目大意

  给一个初始值都是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\n", xCnt * (y2 - y1 + 1) + yCnt * (x2 - x1 + 1) - xCnt * yCnt * 2);
            break;
        }
    }
    return 0;
}

  

 

luogu3801 红色的幻想乡

标签:private   表示   分析   def   turn   print   其它   include   mes   

原文地址:https://www.cnblogs.com/headboy2002/p/9206375.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!