NOIP模拟 栅栏(二维树状数组+矩阵异或)

【题目描述】

小 Z 决定要去 AK PION 2018, 不过在这之前,他要先把家里的牛给养大。

小 Z 家里的牛棚是一个 n ∗ m 的网格,牛均匀地分布在这些网格中。

一开始小 Z 的牛都很小,习性也都相近,所以牛棚中没有栅栏。不过随着时间的推移,不同品种的牛的不同习性逐渐暴露出来,这时候小 Z 会建造栅栏来把不同习性的牛给分隔开。不过,时间久了之后,牛之间也需要交流感情,所以小 Z 有时也可能拆除一些栅栏。

每次小 Z 建造栅栏时,总会先圈出一个矩形内部的牛,然后在这个矩形的边界围一圈栅栏,也就是说一次建造的栅栏会将某个矩阵的内部与外部给隔开。

每次拆除栅栏时,小 Z 只会拆除之前某个时间建造的栅栏,也就是说一次拆除只会拆除之前某个时间建造的一个矩阵边界上的所有栅栏。

不过问题也来了:家里的牛棚太大了,栅栏多了之后,小 Z 根本不知道两个位置的牛是否被栅栏隔开,所以小 Z 想让你帮他解决这个问题。当然,为了方便拆除和建造,小 Z 会保证任意时刻栅栏与栅栏之间互不相交

【输入格式】

第一行两个整数 n,m, q。n,m 表示牛棚的长宽,q 表示接下来发生的事件数。

接下来 q 行,每行有五个整数 p, x1, y1, x2, y2。

如果 p = 1,表示小 Z 在以 (x1, y1) 为左上角,(x2, y2) 为右下角的矩形边界处建造了栅栏。

如果 p = 2,表示小 Z 要拆除以 (x1, y1) 为左上角,(x2, y2) 为右下角的矩形外的栅栏(保证存在)。

如果 p = 3,表示小 Z 想询问你位于 (x1, y1), (x2, y2) 的牛是否被栅栏隔开。

【输出格式】

对于每个询问,如果两个位置没有被隔开,则输出 ”Yes”,否则输出 ”No”。

【样例输入】

5 6 5

1 2 2 4 5

1 3 3 3 3

3 4 4 1 1

2 2 2 4 5

3 1 1 4 4

【样例输出】

Yes

No

【备注】

对于 20% 的数据,n,m, q ≤ 300。

对于 40% 的数据,n,m, q ≤ 2000。

另有 20% 的数据,n = 1。

另有 20% 的数据,q ∈ {1, 3},且所有 3 操作在 1 操作之后。

对于 100% 的数据,n,m ≤ 2000, q ≤ 100000。

【题目分析】

看到题解的我顿时懵了啊,二维树状数组是什么鬼。。。。。

对于每次操作1,我们就rand一个值(不过要注意在windows下rand的值大概只有6w左右,所以就乘一个数再加一个rand,不过linux下就没问题),然后我们将(x1,y1),(x1,y2+1),(x2+1,y1),(x2+1,y2+1)四个点去异或(加也可以)这个rand出的数,因为a^b^b=a,所以可以证明异或的正确性,对于树状数组的区间修改单点查询,与一维的类似。

对于每次操作2,我们开一个map记录每次操作1的rand值,然后直接在4个点处异或(减)回来就行了。

对于每次操作3,我们直接查询前缀异或(和)即可。

【代码~(异或)】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e3+10;

int n,m,q;
int tr[MAXN][MAXN];

int lowbit(int x)
{
	return x&(-x);
}

void change(int x,int y,int v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	  for(int j=y;j<=m;j+=lowbit(j))
	    tr[i][j]^=v;
}

int query(int x,int y)
{
	int ret=0;
	for(int i=x;i;i-=lowbit(i))
	  for(int j=y;j;j-=lowbit(j))
	    ret^=tr[i][j];
	return ret;
}

map<pair<pair<int,int>,pair<int,int> >,int>S;
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	while(q--)
	{
		int cz,x1,y1,x2,y2;
		int v=rand()*6543+rand();
		scanf("%d%d%d%d%d",&cz,&x1,&y1,&x2,&y2);
		if(cz==1)
		{
			change(x1,y1,v);
			change(x1,y2+1,v);
			change(x2+1,y1,v);
			change(x2+1,y2+1,v);
			S[make_pair(make_pair(x1,y1),make_pair(x2,y2))]=v;
		}
		else
		{
			if(cz==2)
			{
				int val=S[make_pair(make_pair(x1,y1),make_pair(x2,y2))];
				change(x1,y1,val);
				change(x1,y2+1,val);
				change(x2+1,y1,val);
				change(x2+1,y2+1,val);
			}
			else
			{
				if(query(x1,y1)==query(x2,y2))
				  printf("Yes\n");
				else
				  printf("No\n");
			}
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值