CF EDU 105 C - 1D Sokoban
阿新 • • 發佈:2022-05-23
C - 1D Sokoban
二分 + 找性質
可分正負的箱子分別討論
本題的關鍵是發現一個重要的性質:因為推箱子這個過程會讓被推到的箱子成為連續的一段,若想讓在特殊位置的箱子最多,則這一段的終點一定要在特殊位置上(起點也可以, 這裡的一定不是說不在特殊位置就取不到最優,而是在特殊位置上的某些情況一定可以取到最優,所以只考慮在特殊位置上的最大值就是全域性的最大值)
證明思路:若結尾不是在特殊位置上,那麼那把它移到最近的特殊位置上一定不會更劣
所以可以列舉每一個特殊位置,讓連續一段箱子的末尾被推到這個點上,看這時的答案是多少
答案 = 這一段箱子覆蓋的特殊位置 + 這一段箱子之後的點中,有多少箱子原來就在特殊位置上
第一項可二分求出,第二項可用字首和求出
#include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <cmath> #include <map> using namespace std; typedef long long ll; const int N = 2e5 + 10; int n, m; int a[N], b[N], c[N], d[N], pre[N]; map<int, bool> mp; int solve(int a[], int b[], int n, int m) { if (n <= 0 || m <= 0) return 0; sort(a + 1, a + n + 1); sort(b + 1, b + m + 1); mp.clear(); for (int i = 1; i <= n; i++) mp[a[i]] = true; for (int i = 1; i <= m; i++) { pre[i] = pre[i-1]; if (mp.count(b[i])) pre[i]++; } int ans = 0; for (int i = 1; i <= m; i++) { int len = upper_bound(a + 1, a + n + 1, b[i]) - a - 1; int r = b[i], l = r - len + 1; int cnt = upper_bound(b + 1, b + m + 1, r) - lower_bound(b + 1, b + m + 1, l); cnt += pre[m] - pre[i]; ans = max(ans, cnt); } return ans; } int main() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); int T; cin >> T; while(T--) { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= m; i++) cin >> b[i]; int ans = 0; int cnta = 0, cntb = 0; //正數 for (int i = 1; i <= n; i++) if (a[i] > 0) c[++cnta] = a[i]; for (int i = 1; i <= m; i++) if (b[i] > 0) d[++cntb] = b[i]; ans = solve(c, d, cnta, cntb); //負數 cnta = 0, cntb = 0; for (int i = 1; i <= n; i++) if (a[i] < 0) c[++cnta] = -a[i]; for (int i = 1; i <= m; i++) if (b[i] < 0) d[++cntb] = -b[i]; ans += solve(c, d, cnta, cntb); cout << ans << endl; } return 0; }