Codeforces Round #439 E. The Untended Antiquity (树状数组+随机化)

Problem

Problem Link

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

1n,m2500

1q100000

1t3

1r1,r2n

1c1,c2m

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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值