POJ-2155:Matrix(二维线段树或二维树状数组)

来源:互联网 发布:三菱plc编程入门教程 编辑:程序博客网 时间:2024/05/29 08:26


题目链接:点击打开链接


题目大意:

给你一个二维表格,执行两种操作,第一种给你左上角和右下角的坐标,让你把这个矩形内的数翻转,即0变成1,1变成0。第二种操作就是查询某个点当前的值。


解题思路:

这道题有两种做法,分别是二维树状数组和二维线段树。以下分别讲解。


树状数组:

二维树状数组很好理解,代码也非常简单,而且最后耗时貌似也比线段树少不少。用一些简单的操作即可实现题目的要求。

假设题目让你更新 p1,q1 p2,q2 那么可以 将p1,q1,后面的点都加1,然后将不符合题目要求的区域减1,即 p2+1,q1   和   p1,q2+1 . 但是这样的话右下角一部分被重复减了两次,因此再将右下角的部分+1,即 p2+1,q2+1 后面加1.   查询的时候直接查询就可以。以下贴树状数组的代码,


#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <cmath>#include <vector>#include <queue>#include <map>#include <algorithm>#include <set>#include <functional>#define lson rt<<1#define rson rt<<1|1using namespace std;typedef long long ll;typedef unsigned long long ULL;int n,m;char c;int d[1005][1005];void add(int i,int j,int x)     //二维树状数组模板,跟一维很像{    while(i<=n)    {        int jj=j;        while(jj<=n)        {            d[i][jj]+=x;            jj += jj&-jj;        }        i += i&-i;    }}int sum(int i,int j){    int s=0;    while(i>0)    {        int jj=j;        while(jj>0)        {            s=s+d[i][jj];            jj -= jj&-jj;        }        i -= i&-i;    }    return s;}int main(){    int QAQ;    scanf("%d",&QAQ);    while(QAQ--)    {        int p1,q1,p2,q2;        scanf("%d%d",&n,&m);        memset(d,0,sizeof(d));    //记得初始化        for(int i=0;i<m;i++)        {            scanf(" %c",&c);            if(c=='C')            {                scanf("%d%d%d%d",&p1,&q1,&p2,&q2);      //将题目要求部分加1                add(p1,q1,1);                add(p1,q2+1,-1);                add(p2+1,q1,-1);                add(p2+1,q2+1,1);            }            if(c=='Q')            {                scanf("%d%d",&p1,&q1);                printf("%d\n",sum(p1,q1)%2);    //输出结果余2            }        }        printf("\n");    }}


线段树部分:


线段树要用二维线段树,也是我第一次写的二维线段树。刚开始是真的不知道怎么写。反正瞎写了一种四分树,反正不知道哪错了疯狂RE,而且后来去网上查了一下。

二维线段树貌似有两种,一种就是刚刚说的四分树,但是四分树某些情况下时间复杂度极高。而且我疯狂RE也不知道为啥。后来学习了另外一种树套树的做法。也就是x对应的每个节点也有一颗线段树,刚开始也一直在想维护的问题,后来上网看了别人的博客才发现不用这么搞,而且更新和查询的方法比较奇妙。


更新的话就是先找到符合要求的x的对应区间,其次更新对应的y线段树。但是这样会在查询的时候造成一个问题,例如,第一次更新 1,1  10,10  。那么假设当前区间恰好为1到10,就会更新当前节点的y线段树。但是如果下一次更新1,1  5,5  更新的就是上个节点的子节点的y线段树,y线段树同样也有这样的问题, 所以查询的时候就要注意,不能随意递归下去,那么如果想解决这样的问题的话就在每一次的的x节点处查询y线段树即可,y线段树也是每个节点的值都加上。这样的话就保证了你查询的值没有遗漏,

买买买   

总体来说可能是这道题目偏简单,实现起来不算太麻烦,但是耗时比树状数组多好多,而且之前写线段树习惯用结构体写,换成这种线段树突然不会用结构体写了,最后忘初始化wa了好多发,可能是我代码太丑的原因。。。


#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <cmath>#include <vector>#include <queue>#include <map>#include <algorithm>#include <set>#include <functional>#define xlson xl,xmid,xt<<1#define xrson xmid+1,xr,xt<<1|1#define ylson yl,ymid,xt,yt<<1#define yrson ymid+1,yr,xt,yt<<1|1using namespace std;typedef long long ll;typedef unsigned long long ULL;const int maxn=1010;int n,m,ans;int tree[maxn<<2][maxn<<2];void update_y(int q1,int q2,int yl,int yr,int xt,int yt)    //更新y线段树{    if(q1<=yl&&yr<=q2)    {        tree[xt][yt]++;        return ;    }    int ymid=(yl+yr)>>1;    if(q1<=ymid)        update_y(q1,q2,ylson);    if(q2>ymid)        update_y(q1,q2,yrson);}void update_x(int p1,int q1,int p2,int q2,int xl,int xr,int xt)     //查找符合要求的区间{    if(p1<=xl&&xr<=p2)      //找到合适的区间就更新y线段树    {        update_y(q1,q2,1,n,xt,1);        return ;    }    int xmid=(xl+xr)>>1;    if(p1<=xmid)        update_x(p1,q1,p2,q2,xlson);    if(p2>xmid)        update_x(p1,q1,p2,q2,xrson);}void query_y(int y,int yl,int yr,int xt,int yt)     //查询y线段树中的值{    ans=ans+tree[xt][yt];       //将每个值都加上防止遗漏    if(yl==yr) return ;    int ymid=(yl+yr)>>1;    if(y<=ymid)        query_y(y,ylson);    if(y>ymid)        query_y(y,yrson);}void query_x(int x,int y,int xl,int xr,int xt)      //查询所有的x线段树{    query_y(y,1,n,xt,1);        //每个x节点都查询y线段树    if(xl==xr) return ;    int xmid=(xl+xr)>>1;    if(x<=xmid)        query_x(x,y,xlson);    if(x>xmid)        query_x(x,y,xrson);}int main(){    int QAQ;    scanf("%d",&QAQ);    while(QAQ--)    {        memset(tree,0,sizeof(tree));    //记得初始化        scanf("%d%d",&n,&m);        char q;        int p1,q1,p2,q2;        for(int i=0;i<m;i++)        {            scanf(" %c",&q);            if(q=='C')            {                scanf("%d%d%d%d",&p1,&q1,&p2,&q2);                update_x(p1,q1,p2,q2,1,n,1);            }            if(q=='Q')            {                scanf("%d%d",&p1,&q1);                ans=0;                query_x(p1,q1,1,n,1);                printf("%d\n",ans%2);            }        }        printf("\n");    }}

原创粉丝点击