Problem
在 n×m 的二维图上,有三种操作:
1 r1 c1 r2 c2
表示沿着 (r1, c1, r2, c2) 所表示的矩形的外边框建围墙。(其中 (r1, c1) 为矩形左上角,(r2, c2) 表示矩形右下角)。2 r1 c1 r2 c2
表示取消 (r1, c1, r2, c2) 所示矩形的围墙。(保证最初图不存在围墙,删除的围墙一定是之前通过操作 1 建立的)。3 r1 c1 r2 c2
表示询问 (r1, c1) 到 (r2, c2) 是否可以不翻越围墙到达。对于每个操作 3,可以输出Yes
,否则输出No
。
Limit
1≤n,m≤2500
1≤q≤100000
1≤t≤3
1≤r1,r2≤n
1≤c1,c2≤m
Idea
基础的二维树状数组可以实现单点更新,区间查询的操作。与一维的类似,显然可以快速转换为区间更新,单点查询。考虑利用状压,则此二维树状数组最多可以维护 64 个不同的围墙(以长整型存储)。但是,显然这仍然不足以处理描述种的 105 的量级。
考虑异或,将二维树状数组的更新操作全部改为异或,对每个新的围墙,都利用随机化产生一个长整型的值代表该围墙内所有数都将异或上该值。则对于操作 3 的询问,一旦查询到的 (r1, c1) 与 (r2, c2) 的异或值不同,显然它们是由多个不同的围墙包围,则不可直接到达(必须翻墙:) )。否则可达。
HINT:注意 C++ 随机数的范围在 [0,RAND_MAX] ,一般 RAND_MAX 普遍为 32767 .故需自行处理随机数,扩大其取值范围。
more: 请不要问关于证明该随机化可行的问题,博主自己也表示想当迷糊,无法给出碰撞量级的证明。
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 2500 + 10;
int n, m, q, t, r1, r2, c1, c2;
long long rnd;
map<long long, long long> mp;
long long rect[N][N];
int lowbit(int x) { return x & -x; }
void add(int x, int y, long long w) {
for(int i=x;i<N;i+=lowbit(i))
for(int j=y;j<N;j+=lowbit(j))
rect[i][j] ^= w;
}
long long get(int x, int y) {
long long ret = 0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
ret ^= rect[i][j];
return ret;
}
long long getRand() { return (RAND_MAX + 1ll) * rand() * rand(); }
int main()
{
scanf("%d %d %d", &n, &m, &q);
for(int i=1;i<=q;i++)
{
scanf("%d %d %d %d %d", &t, &r1, &c1, &r2, &c2);
if(t == 3) {
printf("%s\n", get(r1, c1) == get(r2, c2) ? "Yes" : "No");
continue;
}
if(t == 2) {
rnd = mp[ r1*(1ll<<36) + c1*(1ll<<24) + r2*(1ll<12) + c2 ];
} else {
rnd = getRand();
mp[ r1*(1ll<<36) + c1*(1ll<<24) + r2*(1ll<12) + c2 ] = rnd;
}
add(r1, c1, rnd);
add(r1, c2+1, rnd);
add(r2+1, c1, rnd);
add(r2+1, c2+1, rnd);
}
}