VP Educational Codeforces Round 3


A. USB Flash Drives

题意:给你一个mn个数,你要从中选最少的数使得它们的和大于等于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

待补

posted @   maburb  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示