C. Concat (X-th)
爆搜
代码实现
// n进制枚举
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, k, x;
cin >> n >> k >> x;
vector<string> S(n);
rep(i, n) cin >> S[i];
vector<int> pw(k+1);
pw[0] = 1;
rep(i, k) pw[i+1] = pw[i]*n;
vector<string> cand;
rep(t, pw[k]) {
string ns;
rep(i, k) ns += S[t/pw[i]%n];
cand.push_back(ns);
}
ranges::sort(cand);
cout << cand[x-1] << '\n';
return 0;
}
// 类似dp转移
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, k, x;
cin >> n >> k >> x;
vector<string> S(n);
rep(i, n) cin >> S[i];
vector<string> cand;
cand.push_back("");
rep(i, k) {
vector<string> old;
swap(cand, old);
for (string ns : old) {
rep(i, n) cand.push_back(ns+S[i]);
}
}
ranges::sort(cand);
cout << cand[x-1] << '\n';
return 0;
}
// 递归
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, k, x;
cin >> n >> k >> x;
vector<string> S(n);
rep(i, n) cin >> S[i];
vector<string> cand;
auto f = [&](auto& f, int i, string ns) -> void {
if (i == k) {
cand.push_back(ns);
return;
}
rep(j, n) f(f, i+1, ns+S[j]);
};
f(f, 0, "");
ranges::sort(cand);
cout << cand[x-1] << '\n';
return 0;
}
D. Match, Mod, Minimize 2
由于所有的 \(A_i\) 和 \(B_i\) 均小于 \(M\),因此 \(A_i + B_i \bmod M\) 的结果只能是 \(A_i+B_i\) 或 \(A_i +B_i - M\)
于是问题转化为:最多能组成多少个满足 \(A_i + B_i \geqslant M\) 的数对?
具体做法:
- 将 \(A_i\) 做降序排序
- 将 \(B_i\) 做升序排序
- 为每个 \(A_i\) 寻找最小的满足 \(B_j \geqslant M-A_i\) 的 \(B_j\) 进行配对,可以用双指针来实现
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
void solve() {
int n, m;
cin >> n >> m;
vector<int> a(n), b(n);
rep(i, n) cin >> a[i];
rep(i, n) cin >> b[i];
ranges::sort(a, greater<>());
ranges::sort(b);
ll ans = 0;
rep(i, n) ans += a[i]+b[i];
int bi = 0;
for (int na : a) {
while (bi < n and na+b[bi] < m) bi++;
if (bi == n) break;
ans -= m;
bi++;
}
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
E. Development
本题是 \(\operatorname{floyd}\) 的变种
道路扩建的处理方式和ABC375F相同,都是 \(O(N^2)\)
机场扩建只需添加超级顶点,然后每个机场向这个点连一条边权为 \(T\) 的有向边,再连一条边权为 \(0\) 的反向边,即可采用相同逻辑实现!
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
inline void chmin(ll& a, ll b) { if (a > b) a = b; }
int main() {
int n, m;
cin >> n >> m;
n++;
const ll INF = 1e18;
vector d(n, vector<ll>(n, INF));
rep(i, n) d[i][i] = 0;
rep(i, m) {
int a, b, c;
cin >> a >> b >> c;
chmin(d[a][b], c);
chmin(d[b][a], c);
}
int k, T;
cin >> k >> T;
rep(i, k) {
int a;
cin >> a;
chmin(d[a][0], T);
chmin(d[0][a], 0);
}
rep(k, n)rep(i, n)rep(j, n) chmin(d[i][j], d[i][k]+d[k][j]);
ll ans = 0;
auto add = [&](int a, int b, int sign=1) {
if (a == 0 or b == 0) return;
if (d[a][b] != INF) ans += d[a][b]*sign;
};
rep(i, n)rep(j, n) add(i, j);
auto upd = [&](int a, int b, ll c) {
rep(i, n)rep(j, n) {
add(i, j, -1);
chmin(d[i][j], d[i][a]+c+d[b][j]);
add(i, j);
}
};
int q;
cin >> q;
rep(qi, q) {
int type;
cin >> type;
if (type == 1) {
int a, b, c;
cin >> a >> b >> c;
upd(a, b, c);
upd(b, a, c);
}
else if (type == 2) {
int a;
cin >> a;
upd(a, 0, T);
upd(0, a, 0);
}
else {
cout << ans << '\n';
}
}
return 0;
}
F. Paint Tree 2
树形dp
记 dp[v][i][j] 表示在以点 \(v\) 为根的子树中,选取 \(i\) 条路径,是否选取点 \(v\) 指向父节点的边时的最大值
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
inline void chmax(ll& a, ll b) { if (a < b) a = b; }
int main() {
int n, k;
cin >> n >> k;
vector<int> a(n);
rep(i, n) cin >> a[i];
vector<vector<int>> to(n);
rep(i, n-1) {
int u, v;
cin >> u >> v;
--u; --v;
to[u].push_back(v);
to[v].push_back(u);
}
vector dp(n, vector(k+1, vector<ll>(2)));
auto f = [&](auto& f, int v, int p=-1) -> void {
vector dp2(k+1, vector<ll>(3));
for (int u : to[v]) {
if (u == p) continue;
f(f, u, v);
for (int i = k; i >= 0; --i) for (int j = 2; j >= 0; --j) {
rep(ni, k+1)rep(nj, 2) {
if (i+ni > k or j+nj > 2) break;
chmax(dp2[i+ni][j+nj], dp2[i][j]+dp[u][ni][nj]);
}
}
}
rep(i, k+1) {
chmax(dp[v][i][0], dp2[i][0]);
chmax(dp[v][i][1], dp2[i][1]+a[v]);
if (i < k) chmax(dp[v][i+1][0], dp2[i][2]+a[v]);
}
};
f(f, 0);
ll ans = dp[0][k][0];
cout << ans << '\n';
return 0;
}
G. Concat (1st)
当 \(K = \infty\) 时(经典问题解法):
- 定义字典序规则:当 \(X+Y \leqslant Y+X\) 时,\(X\) 更小
- 答案即为无限玄幻该规则下的最小字符串
当 \(K\) 有限时:
实际答案为上述无限解的前 \(K\) 位的前缀
考虑dp
记 dp[i][j] 表示使用 \(i\) 个字符串时,构造周期字符串前 \(j\) 位的最小总长度
浙公网安备 33010602011771号