VP Educational Codeforces Round 3
A. USB Flash Drives
题意:给你一个\(m\)和\(n\)个数,你要从中选最少的数使得它们的和大于等于\(m\)。
排序后从大到小枚举即可。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);`
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end(), std::greater<int>());
int sum = 0;
for (int i = 0; i < n; ++ i) {
sum += a[i];
if (sum >= m) {
std::cout << i + 1 << "\n";
return;
}
}
}
B. The Best Gift
题意:总共有\(n\)本书分为\(m\)类,告诉你每本书的类型,求有多少种方式选两本书使得两本书的类型不同。
直接统计每一类有多少本书,然后按顺序枚举,每种类型的书都可以和前面所有类型的书一对。
点击查看代码
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<int> cnt(11);
for (int i = 0; i < n; ++ i) {
int x;
std::cin >> x;
++ cnt[x];
}
int ans = 0, sum = 0;
for (int i = 1; i <= m; ++ i) {
ans += cnt[i] * sum;
sum += cnt[i];
}
std::cout << ans << "\n";
}
C. Load Balancing
题意:给你\(n\)个数,你每次可以让一个数加一另一个数减一,求使得极差最小的最小操作次数。
要极差最小,那么最终序列是固定的,我们只需要排序后让大的变成大的,小的变成小的就行,记录需要变的次数。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end(), std::greater<int>());
int ans = 0, sum = std::accumulate(a.begin(), a.end(), 0);
for (int i = 0; i < n; ++ i) {
int x = sum / n;
if (i < sum % n) {
++ x;
}
ans += std::abs(a[i] - x);
}
std::cout << ans / 2 << "\n";
}
D. Gadgets for dollars and pounds
题意:有\(n\)天,\(m\)种物品,你要买\(k\)个物品,给你两个长度为\(n\)的价格数组\(a,b\)代表每一天某一类商品的成本,每个物品要么是\(a\)类要么是\(b\)类,价格为购买时当天的成本乘商品价格。
你有\(s\)块,求最少几天可以买到\(k\)件商品。
考虑二分,如果我们要在第\(mid\)天内买\(k\)件物品,则每一类都应该集中在成本最低的那天购买,于是需要记录\(a,b\)的前缀\(min\),然后我们要枚举买几个\(a\)类和几个\(b\)类,也是贪心的买最便宜的,所以要从大到小排序后记录前缀和。
点击查看代码
void solve() {
int n, m, k, s;
std::cin >> n >> m >> k >> s;
std::vector<i64> a(n), b(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i < n; ++ i) {
std::cin >> b[i];
}
std::vector<std::pair<i64, int>> ta, tb;
for (int i = 0; i < m; ++ i) {
int t, x;
std::cin >> t >> x;
if (t == 1) {
ta.push_back({x, i});
} else {
tb.push_back({x, i});
}
}
std::sort(ta.begin(), ta.end());
std::sort(tb.begin(), tb.end());
int lena = ta.size(), lenb = tb.size();
std::vector<i64> suma(lena + 1), sumb(lenb + 1);
for (int i = 0; i < lena; ++ i) {
suma[i + 1] = suma[i] + ta[i].first;
}
for (int i = 0; i < lenb; ++ i) {
sumb[i + 1] = sumb[i] + tb[i].first;
}
const i64 inf = 1e18;
std::vector<i64> mina(n + 1, inf), minb(n + 1, inf);
for (int i = 0; i < n; ++ i) {
mina[i + 1] = std::min(mina[i], a[i]);
minb[i + 1] = std::min(minb[i], b[i]);
}
auto check = [&](int d) -> std::pair<i64, int> {
i64 va = mina[d], vb = minb[d];
i64 res = inf, p = 0;
for (int i = std::max(0, k - lenb), j = lenb; i <= std::min(k, lena); ++ i) {
while (j > 0 && i + j > k) {
-- j;
}
if (i + j < k) {
break;
}
if (res > suma[i] * va + sumb[j] * vb) {
res = suma[i] * va + sumb[j] * vb;
p = i;
}
}
return {res, p};
};
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid).first <= s) {
r = mid;
} else {
l = mid + 1;
}
}
auto [val, p] = check(l);
if (val > s) {
std::cout << -1 << "\n";
return;
}
int da = 0, db = 0;
for (int i = 0; i < l; ++ i) {
if (a[i] == mina[l]) {
da = i;
break;
}
}
for (int i = 0; i < l; ++ i) {
if (b[i] == minb[l]) {
db = i;
break;
}
}
std::vector<std::pair<int, int>> ans;
for (int i = 0; i < p; ++ i) {
ans.push_back({ta[i].second, da});
}
for (int i = 0; i < k - p; ++ i) {
ans.push_back({tb[i].second, db});
}
std::cout << l << "\n";
for (auto & [x, y] : ans) {
std::cout << x + 1 << " " << y + 1 << "\n";
}
}
E. Minimum spanning tree for each edge
题意:给你一个图,包含某条边的生成树的最小权值。
经典题,考虑建最小生成树,然后根据\(lca\)记录到祖先节点的最大边权。那么对于每条边,如果想要让它更换最小生成树上的边,它加进来会形成一个环,我们需要在这个环里的边选一条最大的删,而这个环上的边恰好都在边连接的两个端点的\(lca\)上。
点击查看代码
struct DSU {
std::vector<int> fa, cnt;
DSU(int _n) {
init(_n);
}
void init(int _n) {
fa.assign(_n, 0);
cnt.assign(_n, 1);
std::iota(fa.begin(), fa.end(), 0);
}
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) {
return false;
}
fa[y] = x;
cnt[x] += cnt[y];
return true;
}
bool same(int x, int y) {
return find(x) == find(y);
}
int size(int x) {
return cnt[find(x)];
}
};
void solve() {
int n, m;
std::cin >> n >> m;
std::vector<std::array<int, 4>> edges(m);
for (int i = 0; i < m; ++ i) {
int u, v, w;
std::cin >> u >> v >> w;
edges[i] = {w, u, v, i};
}
std::sort(edges.begin(), edges.end());
DSU dsu(n + 1);
std::vector<std::vector<std::pair<int, int>>> adj(n + 1);
i64 sum = 0;
for (int i = 0; i < m; ++ i) {
auto & [w, u, v, id] = edges[i];
if (dsu.same(u, v)) {
continue;
}
sum += w;
dsu.merge(u, v);
adj[u].push_back({v, w});
adj[v].push_back({u, w});
}
int lg = std::__lg(n) + 1;
std::vector f(n + 1, std::vector<int>(lg + 1));
std::vector max(n + 1, std::vector<int>(lg + 1, 0));
std::vector<int> d(n + 1);
d[1] = 1;
std::queue<int> q;
q.push(1);
while (q.size()) {
int u = q.front(); q.pop();
for (auto & [v, w] : adj[u]) {
if (d[v] == 0) {
d[v] = d[u] + 1;
f[v][0] = u;
max[v][0] = w;
for (int i = 1; i <= lg; ++ i) {
f[v][i] = f[f[v][i - 1]][i - 1];
max[v][i] = std::max(max[v][i - 1], max[f[v][i - 1]][i - 1]);
}
q.push(v);
}
}
}
auto lca = [&](int x, int y) -> int {
if (d[x] < d[y]) {
std::swap(x, y);
}
int res = 0;
for (int i = lg; i >= 0; -- i) {
if (d[f[x][i]] >= d[y]) {
res = std::max(res, max[x][i]);
x = f[x][i];
}
}
if (x == y) {
return res;
}
for (int i = lg; i >= 0; -- i) {
if (f[x][i] != f[y][i]) {
res = std::max({res, max[x][i], max[y][i]});
x = f[x][i], y = f[y][i];
}
}
res = std::max({res, max[x][0], max[y][0]});
return res;
};
std::vector<i64> ans(m);
for (auto & [w, u, v, id] : edges) {
ans[id] = sum - lca(u, v) + w;
}
for (int i = 0; i < m; ++ i) {
std::cout << ans[i] << "\n";
}
}
F. Frogs and mosquitoes
待补