2021牛客多校#8 F-Robots(bitset)

本文介绍了如何通过bitset数据结构优化查询机器人在网格中的可达性问题,针对不同类型的机器人,提出了一种O(n^4log)的高效算法,通过压缩维度减少了计算复杂度,适用于大规模数据。主要关注了向下、向右和双向移动机器人的路径判断和离线优化技巧。
摘要由CSDN通过智能技术生成

题目链接

传送到队友家里

题目大意

有一个 n ∗ m ( 1 ≤ n , m ≤ 500 ) n*m(1\leq n,m\leq 500) nm(1n,m500)大小的网格,左上角是 ( 1 , 1 ) (1,1) (1,1),右下角是 ( n , m ) (n,m) (n,m),其中有一些无法通过的格子。
有以下三种机器人:
1.只能向下移动的机器人,即只能从 ( x , y ) (x,y) (x,y)移动到 ( x + 1 , y ) (x+1,y) (x+1,y)
2.只能向右移动的机器人,即只能从 ( x , y ) (x,y) (x,y)移动到 ( x , y + 1 ) (x,y+1) (x,y+1)
3.既可以向下也可以向右移动的机器人。
q ( 1 ≤ q ≤ 5 × 1 0 5 ) q(1 \leq q \leq 5 \times10^5) q(1q5×105)次询问,每次询问 t t t型机器人需要从起点 ( x 1 , y 1 ) (x1,y1) (x1,y1)前往终点 ( x 2 , y 2 ) (x2,y2) (x2,y2),判断是否可行。

题解

对于只会向下和向右移动的机器人,只需要求一次前缀即可,若撞到墙则失败。
对于其他情况,我们设 d p x 1 , y 1 , x 2 , y 2 dp_{x_1,y_1,x_2,y_2} dpx1,y1,x2,y2为从 x 1 , y 1 x_1,y_1 x1,y1 x 2 , y 2 x_2,y_2 x2,y2是否可行。
通过计算,我们发现需要进行 O ( n 4 ) O(n^4) O(n4)次操作,超出范围,
q q q次询问复杂度太大,所以我们考虑将其离线操作,将同一起点或终点的可能计算出来。
所以我们使用bitset来优化代码:
①我们可以通过bitset数组删去前两个维度,计算同一终点的情况,
易得dp数组的转移式为 d p x 1 , y 1 = d p x 1 + 1 , y 1 ∣ d p x 1 , y 1 + 1 ∣ ( x 1 , y 1 ) dp_{x1,y1}=dp_{x1+1,y1}|dp_{x_1,y_1+1}|(x_1,y_1) dpx1,y1=dpx1+1,y1dpx1,y1+1(x1,y1)
即当前的可能性或上一步之前的可能性。
我们还可以继续通过bitset来压缩维度,这里我们压去第一维度,则dp转移式更新,
用伪代码实现为:

for(i , n -> 1)
	for(j , n -> 1)
		dp[j]|=dp[j+1]|(i,j)

则代码的复杂度为 O ( n 4 l o g ) O(n^4log) O(n4log),符合要求。
②我们还可以通过bitset删去后两个维度,计算同一起点的情况,
代码实现稍为复杂,先初始化打好起点的方案,然后枚举行,与前一个合并。
由于除核心部分外代码大致相同,故不多赘述,详见代码,这里给出了一份别的选手的代码.

参考代码

//压后两个维度
#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int M=509;
int n,m,q;
char c[M][M];
int X[M][M],Y[M][M];
bool ans[500009];
bitset<M>vis[M];
struct P{
	int rx,ly,ry,id;
};
vector<P>p[M],t[M];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%s",c[i]+1);
		for(int j=1;j<=m;++j)X[i][j]=X[i][j-1]+(c[i][j]=='1'),Y[i][j]=Y[i-1][j]+(c[i][j]=='1');
	}
	scanf("%d",&q);
	for(int i=1,o,lx,ly,rx,ry;i<=q;++i){
		scanf("%d%d%d%d%d",&o,&lx,&ly,&rx,&ry);
		if(o==1){
			if(ly==ry&&rx>=lx&&Y[rx][ly]-Y[lx-1][ly]==0)ans[i]=1;
		}
		else if(o==2){
			if(lx==rx&&ry>=ly&&X[lx][ry]-X[lx][ly-1]==0)ans[i]=1;
		}
		else if(lx<=rx&&ly<=ry)p[lx].eb(P{rx,ly,ry,i});
	}
	for(int l=1;l<=n;++l){
		//将当前行上每个起点出发的询问,存储到终点的每行中 
		for(auto o:p[l])t[o.rx].eb(o);
		//初始化 
		//vis[x][y] 表示 (l,y) 能否走到 (?,x)
		for(int i=1;i<=m;++i)vis[i].reset();
		for(int i=1;i<=m;++i){
			//若(l,i)有障碍物,清零 
			if(c[l][i]=='1')vis[i].reset();
			//(l,i)无障碍物,标记(l,i) 可以走到 (l,i) ,合并上能走到 (l,i-1) 的起点方案 
			else vis[i][i]=1,vis[i]|=vis[i-1];
		}
		//枚举行 
		for(int r=l;r<=n;++r){
			for(int i=1;i<=m;++i){
				//走到 (r,i) 的方案,要合并上能走到 (r,i-1) 的方案 
				vis[i]|=vis[i-1];
				//若 (r,i) 存在障碍物,则无方案 
				if(c[r][i]=='1')vis[i].reset();
				
			}
			for(auto o:t[r]){
				ans[o.id]=vis[o.ry][o.ly];
			}
			t[r].clear();
		}
	}
	for(int i=1;i<=q;++i){
		ans[i]?puts("yes"):puts("no");
	}
	return 0;
}

//压前两个维度
#include <bits/stdc++.h>
using namespace std;

const int N = 505;

int n, m, q;
char mmap[N][N];
bool ans[500010];
bitset<N * N> bs[N];
struct node {
    int id, t, x, y;
};
vector<node> v[N][N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%s", mmap[i] + 1);
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        int t, x1, y1, x2, y2;
        scanf("%d%d%d%d%d", &t, &x1, &y1, &x2, &y2);
        v[x1][y1].push_back({i, t, x2, y2});
    }
    for (int i = n; i; i--)
        for (int j = m; j; j--) {
            if (mmap[i][j] == '0') {
                if (mmap[i][j + 1] == '0') bs[j] |= bs[j + 1];
                bs[j][i * m + j] = 1;
            }
            else bs[j].reset();
            for (node &no : v[i][j]) {
                if (no.t == 1)
                    ans[no.id] = no.y == j && bs[j][no.x * m + no.y];
                else if (no.t == 2)
                    ans[no.id] = no.x == i && bs[j][no.x * m + no.y];
                else
                    ans[no.id] = bs[j][no.x * m + no.y];
            }
        }
    for (int i = 0; i < q; i++) puts(ans[i] ? "yes" : "no");
    return 0;
}
// https://ac.nowcoder.com/acm/contest/view-submission?submissionId=48490864

总结

bitset可用于优化代码减小维度,可以针对较大一点的数据,还是有必要去学习掌握的。
说不定就来考了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值