abc203
C - Friends and Travel costs 168
模拟。
D - Pond 1622
在给定矩阵中找固定尺寸的子矩阵,使中位数最小。
中位数好像经常都是用这个性质吧。
二分答案,在01矩阵上用二维前缀和验证。
E - White Pawn 1750
\([0,2n]\times [0,2n]\) 棋盘上有 \(m\) 个黑子,执白子从 \((0,n)\) 出发。当白子在 \((i,j)\) 时,若 \((i+1,j)\) 上无黑子则可达,若 \((i+1,j-1)\) 上有黑子则可达,若 \((i+1,j+1)\) 上有黑子则可达,没有其它移动方式,问有多少 \(Y\) 满足 \((2n,Y)\) 是可达的。\(n\le 1e9, m\le 2e5\)
脑子秀逗了,一直在想建图。。。
正解是用个集合记录当前行的所有可达点,转移到下一行
转移的写法好像很有讲究,TLE了10发,好像也不是用太多STL的原因,至今不知道为什么。最后很羞耻地抄了别人的写法。。。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n, m;
cin >> n >> m;
map<int, vector<int>> mp;
while (m--) {
int x, y;
cin >> x >> y;
mp[x].push_back(y);
}
set<int> S{n};
for (auto &[_, T] : mp) {
vector<int> add, del;
for (int y : T) if (S.count(y)) del.push_back(y);
for (int y : T) if (S.count(y - 1) || S.count(y + 1)) add.push_back(y);
for (int y : del) S.erase(y);
for (int y : add) S.insert(y);
}
cout << S.size();
return 0;
}
F - Weed 2252
\(n\) 棵草排一排,在除草前可以任意删除至多 \(k\) 棵草。除草步骤如下:每次干掉所有高度 \(>H/2\) 的草,其中 \(H\) 为当前存在的最高的草的高度,重复到除尽为止。要求最小化除草次数,其次最小化除草前的删除次数。
可能我dp实在太拉了,我觉得这个dp还挺妙的。。
显然除草次数是 \(\log_2\) 的,很小。按高度从小到大对草排序,\(f(i,j)\) 表示除尽前 \(i\) 棵草,除草次数是 \(j\) 的最小删除次数。转移:
- 删除第 \(i\) 棵草,\(f(i,j)=f(i-1,j)+1\)
- 不删除第 \(i\) 棵草,由于 \(h_i\) 就是 \(1\sim i\) 中最高的,所以第一次就把 \(>h_i/2\) 的草都干掉,设剩下的草中最高的是第 \(p\) 棵,那么 \(f(i,j)=f(p,j-1)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, m, a[N], f[N][32];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i++) {
if (i <= m) f[i][0] = i;
int p = upper_bound(a + 1, a + 1 + i, a[i] / 2) - a - 1;
for (int j = 1; j <= 31; j++)
f[i][j] = min(f[i - 1][j] + 1, f[p][j - 1]);
}
for (int i = 0; i <= 31; i++) {
if (f[n][i] <= m) {
cout << i << ' ' << f[n][i];
return 0;
}
}
}