矩阵(Matrix)

题目:矩阵(Matrix)
poj的网址:http://poj.org/problem?id=2155
poj题号:2155
时间限制: 3000MS 内存限制: 65536K

题目描述:给定N * N矩阵A,其元素为0或1.A [i,j]表示第i行和第j列中的数字。最初我们有A [i,j] = 0(1 <= i,j <= N)。 我们可以通过以下方式改变矩阵。给定一个左上角是(x1,y1)和右下角是(x2,y2)的矩形,我们使用“C”操作来改变矩形中的所有元素(如果它是一个’0’然后改变它变为’1’,否则将其更改为’0’)。为了维护矩阵的信息,您需要编写一个程序来接收和执行两种指令。

通过使用左上角为(x1,y1)和下方的矩形来改变矩阵来改变矩阵(x1,y1,x2,y2)右角是(x2,y2)。
Q xy(1 <= x,y <= n)查询A [x,y]。

输入描述:输入的第一行是表示测试用例数的整数X(X <= 10)。以下X块表示测试用例。 每个块的第一行包含两个数字N和T(2 <= N <= 1000,1 <= T <= 50000),表示矩阵的大小和指令的数量。以下的T行各自表示具有如上所述的格式“Q x y”或“C x1 y1 x2 y2”的指令。

输出描述:对于每个查询输出一行,它有一个表示A [x,y]的整数。 每两个连续测试用例之间有空白行。

样例输入:
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

样例输出:
1
0
0
1

题解:
题意就是给你个矩阵里面开始全是0,然后给你两种指令: 第一种:‘C x1,y1,x2,y2’就是将左上角为x1,y1,右下角为x2,y2,的这个矩阵内的数字全部翻转,0变1,1变0, :第二种: ‘Q x1 y1’,输出a[x1][y1]的值。
暴力自然就是每次都更新一遍矩阵里的点,最后判断输出便是。但是,我们用更好的办法去解决它。那就是二维树状数组。
怎么用呢?

我们先讨论一维情况,如下:
有一个n卡片的阵列。每个卡片倒放在桌面上。你有两个问题:
1 . T i j (反转从索引i到索引j的卡片,包括第i张和第j张卡——面朝下的卡将朝上;面朝上的卡将朝下)
2 . Q i (如果第i张卡面朝下回答0否则回答1);

解决方法:
解决问题(1和2)的方法有时间复杂度O(log n)。在数组f(长度n + 1)我们存储每个问题T(i, j)——我们设置f[i]++和f[j + 1]- -。对在i和j之间(包括i和j)每个卡k求和f[1] + f[2] + … + f[k],k将递增1,其他全部与前面的一样,结果为和取余2。(因为 如果被改了偶数次,那还是原来的0,反之如果是被改了奇数次,那就是1)
这里写图片描述

好,现在把这个想法拓展到二维上。在一维时,我们改变那两个点时就说给改变的区间定了一个范围,那么二维中我们怎么定这个范围呢?仔细想想,我们在(x1,y1 ),(x2 +1,y1 ),(x1,y2 +1), (x2 +1,y2 +1)这四个点加个一就好,(如果要求的是某个点被修改了几次的话,(x2 +1,y1 ),(x1,y2 +1)这两个点就不是加上一了,而是减去一)

查询(x,y)的时候把这个点的前缀和(所以用树状数组)求出来便是,具体代码中说。

c++代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long int c[1100][1100],cc,x1,x2,y1,y2,n,t,x,y,ans[50010],anss=0;
char ch;
long long int lowbit (long long int i)//求i用2的幂方和表示时,2的最小次幂 
{
	return i&(-i);
}
void update(long long int x,long long int y,int value)//更新树状数组,就是给矩阵的变化定个范围 
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		for(int j=y;j<=n;j+=lowbit(j))
		{
			c[i][j]+=value;
		}
	}
}
long long int getsum(long long int x,long long int y)//求前缀和 
{
	long long int h=0;
	for(long long int i=x;i>=1;i-=lowbit(i))
	{
		for(long long int j=y;j>=1;j-=lowbit(j))
		h+=c[i][j];
	}
	return h;
}
int main()
{
	cin>>cc;//输入有几组数据 
	for(int l=1;l<=cc;l++)
	{
		memset(c,0,sizeof(c));
		memset(ans,0,sizeof(ans));
		cin>>n>>t;//输入矩阵边长和询问的次数 
		for(int i=1;i<=t;i++)
		{
			cin>>ch;
			if(ch=='C')
			{
				scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);   
				update(x1,y1,1);//给四个点的值加上一,这里可能会有疑问,在刚刚的一维讲解中明明是有点要减一的啊,为啥这里不用。因为在这题中我们只需要知道的是这点被改过的次数是奇数还是偶数,它在求前缀和时加了两遍一取余2,跟加上一再加上负一取余2是没区别的(仅仅是这题可以,如果要求的是被改了多少次就不行)。 
				update(x1,y2+1,1);
				update(x2+1,y1,1);
				update(x2+1,y2+1,1);
			}
			else 
			{
				scanf("%lld%lld",&x,&y);  
				cout<<(getsum(x,y))%2<<endl;//输出前缀和取余2,如果被改了偶数次,那还是原来的0,反之如果是被改了奇数次,那就是1 
			}
		}
		cout<<endl;//这里的换行我也不知道为什么要这样,但是这里不换行在poj上就过不了啊 
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值