1. 程式人生 > 其它 >2021牛客暑期多校訓練營第8場 F題(兩種做法)

2021牛客暑期多校訓練營第8場 F題(兩種做法)

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";
    }

}