C. Robot Factory
用前 \(k\) 小的头和前 \(k\) 大的身体按递增顺序做匹配
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, m, k;
cin >> n >> m >> k;
vector<int> h(n), b(m);
rep(i, n) cin >> h[i];
rep(i, m) cin >> b[i];
sort(h.begin(), h.end());
sort(b.begin(), b.end());
vector<int> kh(h.begin(), h.begin()+k);
vector<int> kb(b.end()-k, b.end());
rep(i, k) if (kh[i] > kb[i]) {
puts("No");
return 0;
}
puts("Yes");
return 0;
}
D. Robot Customize
一开始不妨将所有零件安装在身体上,然后对于所有 \(H_i > B_i\) 的零件做 \(01\) 背包(价值为 \(H_i-B_i\)),为了满足头部重量不超过身体重量,将背包容量设置为 \(\frac{1}{2}\sum W_i\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
inline void chmax(ll& a, ll b) { if (a < b) a = b; }
int main() {
int n;
cin >> n;
int W = 0; ll sumb = 0;
vector<P> ps;
rep(i, n) {
int w, h, b;
cin >> w >> h >> b;
W += w;
sumb += b;
if (h > b) ps.emplace_back(w, h-b);
}
const ll INF = 1e18;
n = ps.size();
W /= 2;
vector<ll> dp(W+1);
rep(i, n) {
vector<ll> old(W+1, -INF);
swap(dp, old);
auto [w, v] = ps[i];
rep(j, W+1) {
chmax(dp[j], old[j]);
if (j+w <= W) chmax(dp[j+w], old[j]+v);
}
}
ll ans = dp[W]+sumb;
cout << ans << '\n';
return 0;
}
E. Reflection on Grid
因为更换镜子时可以任意调整朝向,所以所有需要二次访问的解都能无损地改造成一次访问的解。因此只要考虑简单路径的情况即可。以当前位置坐标和当前朝向作为状态,用 \(01\text{bfs}\) 求解。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
const int di[] = {-1, 0, 1, 0};
const int dj[] = {0, -1, 0, 1};
void solve() {
int h, w;
cin >> h >> w;
vector<string> s(h);
rep(i, h) cin >> s[i];
const int INF = 1001001001;
vector dist(h, vector(w, vector<int>(4, INF)));
deque<tuple<int, int, int, int>> q;
auto push = [&](int i, int j, int v, int d, int cost) {
d += cost;
if (dist[i][j][v] <= d) return;
dist[i][j][v] = d;
if (cost == 0) q.emplace_front(d, i, j, v);
else q.emplace_back(d, i, j, v);
};
push(0, 0, 3, 0, 0);
int ans = INF;
while (q.size()) {
auto [d, i, j, v] = q.front(); q.pop_front();
if (dist[i][j][v] != d) continue;
int ov = v;
if (s[i][j] == 'B') ov ^= 1;
if (s[i][j] == 'C') ov ^= 3;
rep(u, 4) {
int ni = i+di[u], nj = j+dj[u];
int cost = ov==u?0:1;
if (ni == h-1 and nj == w) {
ans = min(ans, d+cost);
}
if (ni < 0 or ni >= h or nj < 0 or nj >= w) continue;
push(ni, nj, u, d, cost);
}
}
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
F. Almost Sorted 2
对于一个数 \(X\),只能插进 \([x-D, X-1]\) 之间的空隙,或者插在末尾
比较简单的组合计数,假设在插入 \(X\) 之前已经有 \(K\) 个位于 \([x-D, X-1]\) 之间的数了,且 \(X\) 有 \(m\) 个,那么这 \(m\) 个 \(X\) 的插法数就是 \(\binom{m+k-1}{m}\)。我们利用乘法原理,就能算出最终的答案。
背后的算法思想是插入 \(\text{dp}\)
双倍经验:P6522 (注:这个不需要去重)
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using mint = modint998244353;
struct modinv {
int n; vector<mint> d;
modinv(): n(2), d({0,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(-d[mint::mod()%n]*(mint::mod()/n)), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} invs;
struct modfact {
int n; vector<mint> d;
modfact(): n(2), d({1,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(d.back()*n), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} facts;
struct modfactinv {
int n; vector<mint> d;
modfactinv(): n(2), d({1,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(d.back()*invs(n)), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} ifacts;
mint comb(int n, int k) {
if (n < k || k < 0) return 0;
return facts(n)*ifacts(k)*ifacts(n-k);
}
int main() {
int n, d;
cin >> n >> d;
const int M = 1e6+5;
vector<int> c(M);
rep(i, n) {
int a;
cin >> a;
c[a]++;
}
mint ans = 1;
int k = 1;
rep(i, M) {
ans *= comb(c[i]+k-1, c[i]);
k += c[i];
if (i >= d) k -= c[i-d];
}
cout << ans.val() << '\n';
return 0;
}
G. One Time Swap 2
参考 StarSilk
浙公网安备 33010602011771号