2021牛客暑期多校训练营第8场 F题(两种做法)

2021牛客暑期多校训练营8 F题(两种做法)

链接:https://ac.nowcoder.com/acm/contest/11259/F
来源:牛客网

题意:

有一个 \(n*m\)​ 的货运中心,每个格子为0或1,1代表有障碍不可通行。

有3种机器人,一种只能向右走,一种只能向下走,一种可以向下或向右走。

现在有 \(Q\) 个询问,每个询问包含了机器人型号,一个起点坐标和一个终点坐标,请问机器人能否将货物从起点运送到终点

输入
4 4
0000
0000
0001
0010
4
1 1 1 1 4
2 2 1 2 4
3 1 1 3 3
3 3 3 4 4
输出
no
yes
yes
no
补题:

我首先花了一晚上去看某十字的代码,理解了一半

另一半感觉像是某种玄学的按64位分块后再状压,不是很理解,不过这部分可以用bitset搞过去

从右下往左上枚举,设 \(f[i][j][k]\)​表示枚举到当前位置时(注意 \(i\) 不是当前行,\(j\) 是当前列),能否走到 \((i,k)\)​ 这个位置

第三维 \(k\)​ 是可以被压掉的,把 \(f[i][j]\)​ 设为bitset,那么其实有 \(f[i][j] = f[i][j] \quad or\quad f[i][j+1]\)​​

这个状态很玄妙。。。可以结合代码手动跑感受一下

看不懂也没事,标答的分治做法更好理解(也更快)

bitset<502> f[505][505];
int mp[505][505], ans[500015];
int dw[505][505], rt[505][505];

struct ques{
    int x2,y2,id;
};
vector<ques> ot[505][505];

void solve(){
    int n,m,Q;
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        string s;
        cin >> s;
        for(int j=0;j<m;j++){
            mp[i][j+1] = s[j] - '0';
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            rt[i][j] = rt[i][j-1] + mp[i][j];
            dw[i][j] = dw[i-1][j] + mp[i][j];
        }
    }

    cin >> Q;
    for(int i=1;i<=Q;i++){
        int tp, x1, y1 ,x2 ,y2;
        cin >> tp >> x1 >> y1 >> x2 >> y2;
        if(x2 < x1 || y2 < y1) ans[i] = 0;
        else if(tp==1){
            ans[i] = (y1 == y2 && dw[x1][y1] == dw[x2][y2]);
        }else if(tp==2){
            ans[i] = (x1 == x2 && rt[x1][y1] == rt[x2][y2]);
        }else{
            ot[x1][y1].push_back({x2, y2, i});
        }
    }

    for(int i=0;i<=n+2;i++) for(int j=0;j<=m+2;j++) f[i][j] = 0;
    for(int i=n;i>=1;i--){
        for(int j=m;j>=1;j--){
            if(mp[i][j]){
                for(int k=n;k>=i;k--){
                    f[k][j] = 0;
                }
            }else{
                f[i][j][j] = true;
                if(j!=m) for(int k=n;k>=i;k--){
                    f[k][j] |= f[k][j+1];
                }
            }
            for(int k=0;k<ot[i][j].size();k++){
                int xv = ot[i][j][k].x2, yv = ot[i][j][k].y2;
                ans[ot[i][j][k].id] = f[xv][j][yv];
            }
        }
    }

    for(int i=1;i<=Q;i++){
        if(ans[i]) cout << "yes\n";
        else cout << "no\n";
    }
}
标答做法(分治):

对整个矩阵竖着来一刀,划分成 \([l, mid]\)​, \([mid+1, r]\)

考虑处理横跨左右的询问,以 \(mid\)​ 这一列为枢纽

对所有左边的点,处理出从它开始能走到中线上哪些位置

对所有右边的点,处理出中线上哪些点开始能走到这个它

显然任何横跨两边的询问,如果可行那么它们必然存在一个枢纽

其他询问分治做就可以啦

int n,m,Q;
bitset<502> f[505][505];
int mp[505][505], ans[500015];
int dw[505][505], rt[505][505];

struct ques{
    int x2,y2,id;
};
vector<ques> ot[505][505];

void CDQ(int l, int r){

    if(l==r){//边界处理
        for(int i=n;i>=1;i--){
            if(mp[i][l]) f[i][l] = 0;
            else{
                f[i][l][i] = 1;
                f[i][l] |= f[i+1][l];
            }
            for(int k=0;k<ot[i][l].size();k++){
                int xv = ot[i][l][k].x2, yv = ot[i][l][k].y2;
                if(l == yv && xv >= i){
                    ans[ot[i][l][k].id] = f[i][l][xv];
                }
            }
        }
        for(int i=1;i<=n;i++) f[i][l] = 0;
        return;
    }
    int mid = (l+r)/2;
    CDQ(l, mid);
    CDQ(mid+1, r);

    for(int i=1;i<=n;i++){//计算右半边每个点能由哪些中点到达
        if(mp[i][mid]) f[i][mid] = 0;
        else{
            f[i][mid][i] = 1;
            f[i][mid] |= f[i-1][mid];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=mid+1;j<=r;j++){
            if(mp[i][j]) f[i][j] = 0;
            else{
                f[i][j] |= f[i-1][j];
                f[i][j] |= f[i][j-1];
            }
        }
    }

    for(int i=1;i<=n;i++) f[i][mid] = 0;
    for(int i=n;i>=1;i--){//计算左半边每个点能到达哪些中点
        if(mp[i][mid]) f[i][mid] = 0;
        else{
            f[i][mid][i] = 1;
            f[i][mid] |= f[i+1][mid];
        }
    }
    for(int i=n;i>=1;i--){
        for(int j=mid-1;j>=l;j--){
            if(mp[i][j]) f[i][j] = 0;
            else{
                f[i][j] |= f[i+1][j];
                f[i][j] |= f[i][j+1];
            }
        }
    }

    for(int i=1;i<=n;i++){//处理答案
        for(int j=l;j<=mid;j++){
            for(int k=0;k<ot[i][j].size();k++){
                int xv = ot[i][j][k].x2, yv = ot[i][j][k].y2;
                if(mid < yv && yv <= r){
                    ans[ot[i][j][k].id] = (f[i][j] & f[xv][yv]).count();
                }
            }
        }
    }

    for(int i=1;i<=n;i++) for(int j=l;j<=r;j++) f[i][j] = 0;//撤销
}

void solve(){
    cin >> n >> m;
    for(int i=1;i<=n;i++){
        string s;
        cin >> s;
        for(int j=0;j<m;j++){
            mp[i][j+1] = s[j] - '0';
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            rt[i][j] = rt[i][j-1] + mp[i][j];
            dw[i][j] = dw[i-1][j] + mp[i][j];
        }
    }

    cin >> Q;
    for(int i=1;i<=Q;i++){
        int tp, x1, y1 ,x2 ,y2;
        cin >> tp >> x1 >> y1 >> x2 >> y2;
        if(x2 < x1 || y2 < y1) ans[i] = 0;
        else if(tp==1){
            ans[i] = (y1 == y2 && dw[x1][y1] == dw[x2][y2]);
        }else if(tp==2){
            ans[i] = (x1 == x2 && rt[x1][y1] == rt[x2][y2]);
        }else{
            ot[x1][y1].push_back({x2, y2, i});
        }
    }

    CDQ(1, m);

    for(int i=1;i<=Q;i++){
        if(ans[i]) cout << "yes\n";
        else cout << "no\n";
    }

}
posted @ 2021-08-10 19:18  lx_tyin  阅读(114)  评论(0编辑  收藏  举报