原题链接:https://ac.nowcoder.com/acm/contest/11259/F
题意
有一个由01组成的n*m的图,0代表可以通过,1代表不能通过,每个机器人有不同的走法
- 只能从(x,y)走到(x+1,y)
- 只能从(x,y)走到(x,y+1)
- 既可以从(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;
}