POJ 2155 Matrix
(树套树,二维树状数组,二维线段树)
Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1 <= i, j <= N).
We can change the matrix in the following way. Given a rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2), we change all the elements in the rectangle by using "not" operation (if it is a '0' then change it into '1' otherwise change it into '0'). To maintain the information of the matrix, you are asked to write a program to receive and execute two kinds of instructions.
1. C x1 y1 x2 y2 (1 <= x1 <= x2 <= n, 1 <= y1 <= y2 <= n) changes the matrix by using the rectangle whose upper-left corner is (x1, y1) and lower-right corner is (x2, y2).
2. Q x y (1 <= x, y <= n) querys A[x, y].
Input
The first line of the input is an integer X (X <= 10) representing the number of test cases. The following X blocks each represents a test case.
The first line of each block contains two numbers N and T (2 <= N <= 1000, 1 <= T <= 50000) representing the size of the matrix and the number of the instructions. The following T lines each represents an instruction having the format "Q x y" or "C x1 y1 x2 y2", which has been described above.
Output
For each querying output one line, which has an integer representing A[x, y].
There is a blank line between every two continuous test cases.
Sample Input
1
2 10
C 2 1 2 2
Q 2 2
C 2 1 2 1
Q 1 1
C 1 1 2 1
C 1 2 1 2
C 1 1 2 2
Q 1 1
C 1 1 2 1
Q 2 1
Sample Output
1
0
0
1
题目大意:
有一个n*n的矩形平面,里面每个点初始为0,有两种操作:
1.将左上角为(x1,y1),右下角为(x2,y2)的矩形区域中的每个点,把0变成1,把1变成0
2.查询点(x,y)当前的值
Sol: 根本思想,找一个点被哪些矩形覆盖,即它在哪些矩形中。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep(i,n) for(int i=0;i<n;++i)
#define mes(s,c) memset(s,c,sizeof(s))
const int maxn=1001;
using namespace std;
int sum[maxn<<2][maxn<<2];
int n,m;
int ans;
void Subbuild(int st,int l,int r,int rt)
//针对每一行,建立一个线段树
{
sum[st][rt]=0;
if(l==r) return ;
int m=(l+r)>>1;
Subbuild(st,lson);
Subbuild(st,rson);
}
void Build(int l,int r,int rt)
//建立整个线段树,一个矩形
{
Subbuild(rt,1,n,1);
if(l==r) return;
int m=(l+r)>>1;
Build(lson);
Build(rson);
}
void Subupdate(int L,int R,int st,int l,int r,int rt)
//st这个参数一直不变的,它对应x轴
//rt在调用时会发生变化,它对应y轴
{
if(L<=l&&r<=R)
{
sum[st][rt]^=1;
return ;
}
int m=(l+r)>>1;
if(L<=m)
Subupdate(L,R,st,lson);
if(m<R)
Subupdate(L,R,st,rson);
}
void Update(int x1,int y1,int x2,int y2,int l,int r,int rt)
//[x1,y1]和[x2,y2]分别代表查询区域的左上角和右下解
//[l,r]代表线段上x轴的范围, rt代表对应的根结号
//在更新矩形的时候,先根据x1,x2找它们在矩形的哪个行号区域
{
if(x1<=l&&r<=x2)
//找到了对应的x轴区间,于是在y轴上进行修改
{
Subupdate(y1,y2,rt,1,n,1);
return ;
}
int m=(l+r)>>1;
if(x1<=m) // 如果落在矩形的上半部
Update(x1,y1,x2,y2,lson);
if(m<x2)
Update(x1,y1,x2,y2,rson);
}
void Subquery(int L,int R,int st,int l,int r,int rt)
//[L,R]要查询的区域,根据它们找其在y轴的左边还是右边
//st代表是属于x轴上哪个点
//[l,r]代表当前y轴上的区域
//rt代表对应[l,r]区轴上根结点编号
{
ans^=sum[st][rt];
if(l==r) return;
int m=(l+r)>>1;
if(R<=m) //落在y轴的左边
Subquery(L,R,st,lson);
else //落在y轴的右边
Subquery(L,R,st,rson);
}
void Query(int L,int R,int l,int r,int rt)
//初始调用为Query(x,y,1,n,1);
//[L,R]代表要查询的点
//[l,r]代表x轴上的区域,rt是其根
{
Subquery(L,R,rt,1,n,1);
//要查询的点[L,R]必然是落在当前根rt(描述所有x轴)
//[1,n]代表所有列,1是其根
//也就是说当前点一定是落在整个矩形中的
//如果它是上半部的某个点,也必然是落在整个上半部中的
//就这样递归找下去,看这个点落在多少个矩形中
if(l==r) return ;
int m=(l+r)>>1;
if(L<=m) //落在矩形的上半部
Query(L,R,lson);
else //落在矩形的下半部
Query(L,R,rson);
}
int main()
{
int T;
cin>>T;
while(T--){
scanf("%d%d",&n,&m);
Build(1,n,1);
char op[10];
int x1,y1,x2,y2,x,y;
while(m--){
scanf("%s",op);
if(op[0]=='C')
//修改操作
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
Update(x1,y1,x2,y2,1,n,1);
}
else
{
ans=0;
scanf("%d%d",&x,&y);
Query(x,y,1,n,1);
printf("%d\n",ans);
}
}
if(T) puts(" ");
}
return 0;
}
//注意这题是矩形修改,然后单点询问,所以要看这个点被哪些矩形包括了.
#include <stdio.h>
#include <string.h>
#define MAXN 1005
#define mem(a) memset(a, 0, sizeof(a))
bool tree[MAXN<<2][MAXN<<2];
int X, N, T;
int num, X1, X2, Y1, Y2;
char ch[10];
void updatey(int yl,int yr,int xp,int yp)
{
if(Y1<=yl && yr<=Y2)
{
tree[xp][yp]=!tree[xp][yp];
return;
}
int mid=(yl+yr)>>1;
if(Y1<=mid)
updatey(yl,mid,xp,yp<<1);
if(Y2>mid )
updatey(mid+1,yr,xp,yp<<1|1);
}
void updatex(int xl,int xr,int xp)
{
if(X1<=xl && xr<=X2)//目前所在区间在所要更新区间内部
{
updatey(1,N,xp,1);
return;
}
int mid=(xl+xr)>>1;
if(X1<=mid)
updatex(xl,mid,xp<<1);
if(X2>mid)
updatex(mid+1,xr,xp<<1|1);
}
void queryy(int yl,int yr,int xp,int yp)
{
num+=tree[xp][yp];//xp代表xp轴即外面那棵树,yp代表内层那棵树
if(yl==yr)
return;
int mid=(yl+yr)>>1;
if(Y1<=mid)
queryy(yl,mid,xp,yp<<1);
else
queryy(mid+1,yr,xp,yp<<1|1);
}
void queryx(int xl,int xr,int xp)
{
queryy(1,N,xp,1);
//对于xp这棵x轴上的线段线段树,查询y这个线段树上的第1个点,其管辖范围是1,N
if(xl==xr)
return;
int mid=(xl+xr)>>1;
if(X1<=mid) //看目标点落在整个矩形的上半部分还是下半部分
queryx(xl,mid,xp<<1);
else
queryx(mid+1,xr,xp<<1|1);
}
int main()
{
while(~scanf("%d", &X))
while(X--)
{
mem(tree);
scanf("%d %d%*c", &N,&T);
for(int i=0;i<T;i++)
{
scanf("%s%d%d",ch,&X1,&Y1);
if(ch[0]=='Q')
//统计(x1,y1)这个点被改了多少次,
//由于我们在修改时矩形内所有点变换,所以要看哪些矩形是包含当前这个点的
{
num=0;
queryx(1,N,1);
//1和N代表是x轴上,1代表外层线段树上第一个点,它管辖所有的点
printf("%d\n",num%2);}
else
{
scanf("%d%d",&X2,&Y2);
//修改操作[X1,Y1]是左上角,[X2,Y2]是右下角
updatex(1,N,1);
}
}
if(X) printf("\n");
}
return 0;
}

浙公网安备 33010602011771号