2021牛客暑期多校训练营8 F.Robots 离线分治+bitset

原题链接:https://ac.nowcoder.com/acm/contest/11259/F

题意

有一个由01组成的n*m的图,0代表可以通过,1代表不能通过,每个机器人有不同的走法

  1. 只能从(x,y)走到(x+1,y)
  2. 只能从(x,y)走到(x,y+1)
  3. 既可以从(x,y)走到(x+1,y),也可以从(x,y)走到(x,y+1)

每次询问给定一个开始和结束坐标以及走法,问是否可以走到。

解法一

赛中有一种非常神奇的 O ( N 4 w ) O(\frac{N^4}{w}) O(wN4)的做法,不知道为什么给评测机跑过去了,而且还很快。具体就是直接模拟从左上到右下的走法,用bitset存当前列可以被哪些点走到,离线询问。

code(869ms)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ul;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;

#define lowbit(i) (i & -i)
#define Debug(x) cout << (x) << endl
#define fi first
#define se second
#define mem memset
#define endl '\n'

bitset<505*505> vis[505];
char mp[505][505];
int n, m, ans[N];
struct node {
    int id, x, y, op;
};
vector<node> G[505][505];
inline void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
        }
    }
    int q; cin >> q;
    for (int i = 1; i <= q; i++) {
        int op, stx, sty, edx, edy;
        cin >> op >> stx >> sty >> edx >> edy;
        G[edx][edy].push_back({i, stx, sty, op});
    }
    //vis[0].reset();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (mp[i][j] == '0') {
                vis[j] |= vis[j-1];
                vis[j][i * m + j] = 1;
            } else {
                vis[j].reset();
            }
            for (auto it : G[i][j]) {
                if (it.op == 1 && it.y == j && vis[j][it.x * m + it.y]) {
                    ans[it.id] = 1;
                } else if (it.op == 2 && it.x == i && vis[j][it.x * m + it.y]) {
                    ans[it.id] = 1;
                } else if (it.op == 3 && vis[j][it.x * m + it.y]) {
                    ans[it.id] = 1;
                }
            }
        }
    }
    for (int i = 1; i <= q; i++) cout << (ans[i] ? "yes" : "no") << endl;
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}

解法二(正解)

我们对询问分类一下,1和2的走法都是可以直接预处理得到答案的,剩下也只有3的情况。我们考虑分治来优化掉n。

对于横跨mid的两个x1,x2我们可以从mid开始向两侧覆盖,记录vis[i][j]代表可以从i,j走到的所有纵坐标为mid的横坐标,如果两点的vis[i][j]有交集,即起点和终点都可以到达mid上某点,那么起点是可以到达终点的。我觉得这个转化是本题的难点,可以从 O ( N 4 / w ) O(N^4/w) O(N4/w)变成 O ( N 3 l o g N / w ) O(N^3logN/w) O(N3logN/w)的关键。对于x1,x2在同一边的情况,继续分治下去。

code(275ms)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ul;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;

#define lowbit(i) (i & -i)
#define Debug(x) cout << (x) << endl
#define fi first
#define se second
#define mem memset
#define endl '\n'

char mp[505][505];
int n, m, ans[N];
int L[505][505], U[505][505];
struct node {
    int id, x1, y1, x2, y2, op;
};
vector<node> Q;
bitset<505> vis1[505][505], vis2[505][505];
void cdq(int l, int r, vector<node> &q) {
    if (l > r) return;
    int mid = (l + r) >> 1;
    for (int i = mid; i >= l; i--) {
        for (int j = m; j >= 1; j--) {
            vis1[i][j].reset();
            if (mp[i][j] == '1') continue;
            if (i == mid) vis1[i][j][j] = 1;
            else vis1[i][j] = vis1[i+1][j];
            vis1[i][j] |= vis1[i][j+1];
        }
    }
    for (int i = mid; i <= r; i++) {
        for (int j = 1; j <= m; j++) {
            vis2[i][j].reset();
            if (mp[i][j] == '1') continue;
            if (i == mid) vis2[i][j][j] = 1;
            else vis2[i][j] = vis2[i-1][j];
            vis2[i][j] |= vis2[i][j-1];
        }
    }
    vector<node> vl, vr;
    for (auto it : q) {
        if (it.x2 < mid) vl.push_back(it);
        else if (it.x1 > mid) vr.push_back(it);
        else {
            ans[it.id] = (vis1[it.x1][it.y1] & vis2[it.x2][it.y2]).any();
        }
    }
    cdq(l, mid - 1, vl);
    cdq(mid+1, r, vr);
}
inline void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> mp[i][j];
            if (mp[i][j] == '0') L[i][j] = L[i][j - 1] + 1;
        }
    }
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i <= n; i++) {
            if (mp[i][j] == '0') U[i][j] = U[i - 1][j] + 1;
        }
    }
//    for (int i = 1; i <= n; i++) {
//        for (int j = 1; j <= m; j++) {
//            cout << U[i][j] << ' ';
//        }
//        cout << endl;
//    }
    int q; cin >> q;
    for (int i = 1; i <= q; i++) {
        int op, x1, x2, y1, y2;
        cin >> op >> x1 >> y1 >> x2 >> y2;
        if (x1 > x2 || y1 > y2) {
            ans[i] = 0;
            continue;
        }
        if (op == 1) {
            if (y1 != y2 || U[x2][y2] < x2 - x1) ans[i] = 0;
            else ans[i] = 1;
        } else if (op == 2) {
            if (x1 != x2 || L[x2][y2] < y2 - y1) ans[i] = 0;
            else ans[i] = 1;
        } else {
            Q.push_back({i, x1, y1, x2, y2, op});
        }
    }
    cdq(1, n, Q);

    for (int i = 1; i <= q; i++) cout << (ans[i] ? "yes" : "no") << endl;
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值