CF609

CF edu3

A. USB Flash Drives

思路

贪心, 用最大的U盘装, 如果装不下就用下一个继续, 统计答案即可。

代码

#include<bits/stdc++.h>

using namespace std;

int n, m;
int a[110];
bool cmp(int a, int b)
{
	return a > b;
}
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	sort(a+1, a+n+1, cmp);
	for(int i = 1; i <= n; i++)
	{
		if(m <= a[i])
		{
			cout << i << endl;
			break;
		}
		m -= a[i];
	}
	return 0;
}

大佬用的vector并统计前缀和, 细节看代码


#pragma GCC optimize("O3")

#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <string>
#include <assert.h>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <random>
#include <cmath>

#define ll long long
#define ld long double
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define int long long
using namespace std;

const int INF = 2e9;
const int MOD = 1e9 + 7;
const int MB = 20;

void solve() {
	int n, m;
	cin >> n >> m;
	vector<int> a(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a.rbegin(), a.rend());
	int summ = 0;
	for (int i = 0; i < n; i++) {
		if (summ + a[i] >= m) {
			cout << i + 1;
			return;
		}
		summ += a[i];
	}
}

signed main() {
	/*
	freopen("search.in", "r", stdin);
	freopen("search.out", "w", stdout);
	*/
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cout.precision(12);
	solve();
}

B. The Best Gift

思路

组合计数题, 我们可以看到一本书可以和其他的书组合, 当且仅当这一本书与它的类型不同, 那么这样的书共有:n本书减去当前这本类型的书的数量。这样我们可以把每种类型里的每本书都这样枚举一遍, 在把答案加起来即可。
注意, 这样的话对于两本不同类型的书(i,j), 其在i被枚举了一次, 在j被枚举了一次, 重复记了两次, 应减去一次。

代码

#include<bits/stdc++.h>

using namespace std;

int n, m;
int cnt[20];
int a[200010];
long long ans = 0;
int main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		cnt[a[i]]++;
	}
	/*for(int i = 1; i <= m; i++)
	{
		cout << i << ": " << cnt[i] << endl;
	}*/
	for(int i = 1; i <= n; i++)
	{
		ans = ans + (n - cnt[a[i]]);
	//	cout << a[i] << " " << cnt[a[i]] << " ";
	//	cout << ans << endl;
	}
	//cout << ans << endl;
	ans = ans / 2;
	cout << ans << endl;
	return 0;
}

大佬直接把所有种类相同的数一次算, 并且每次及时更新, 这样就不用考虑重复的问题了


#pragma GCC optimize("O3")

#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <string>
#include <assert.h>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <random>
#include <cmath>

#define ll long long
#define ld long double
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define int long long
using namespace std;

const int INF = 2e9;
const int MOD = 1e9 + 7;
const int MB = 20;

void solve() {
	int n, m;
	cin >> n >> m;
	vector<int> a(m);
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		a[x - 1]++;
	}
	int ans = 0;
	int sum = n;
	for (int i = 0; i < m; i++) {
		if (!a[i]) continue;
		ans += a[i] * (sum - a[i]);
		sum -= a[i];
	}
	cout << ans;
}

signed main() {
	/*
	freopen("search.in", "r", stdin);
	freopen("search.out", "w", stdout);
	*/
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cout.precision(12);
	solve();
}

C. Load Balancing

思路

最优策略就是把所有数都变成他们的平均数,如果为小数, 那么就有总和除以n个平均数, 剩下的都是平均数加一。遇到这种情况的话, 我们可以给原数组排一下序, 这样就能保证他们到达平均数的距离最小了。
统计的话其实你就统计比要变成目标小的数变成目标的和, 这样比目标大的数也会相应的变化。

#include<bits/stdc++.h>

using namespace std;

int n;
int a[100010];
int sum;
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];	
		sum += a[i];
	}
	if(sum % n == 0)
	{
		int cnt = sum / n;
		int pos = 0;
		int impos = 0;
		for(int i = 1; i <= n; i++)
		{
			if(a[i] < cnt)
			{
				pos += cnt - a[i];
			}
			else if(a[i] > cnt)
			{
				impos += a[i] - cnt;
			}
			
		}
		cout << min(pos, impos) + abs(pos - impos) << endl;
	}
	else
	{
		int x = sum / n;
		int y = x+1;
		int cntb = sum % n;
		int cnta = n - cntb;
		int ans = 0;
		sort(a+1, a+n+1);
		int pos = 0;
		int impos = 0;
		for(int i = 1; i <= cnta; i++)
		{
			if(a[i] < x)
			{
				pos += x - a[i];
			}
			else if(a[i] > x)
			{
				impos += a[i] - x;
			}
		}
		for(int i = cnta+1; i <= n; i++)
		{
			if(a[i] < y)
			{
				pos += y - a[i];
			}
			else if(a[i] > y)
			{
				impos += a[i] - y;
			}
		}
		cout << min(pos, impos) + abs(pos - impos) << endl;
	}
	return 0;
}

大佬直接硬算平均数, 剩下的余数就是变成比平均数+1的数的个数, 我们可以让比他大的数变, 这样的话距离就会比原来变成平均数少1.


#pragma GCC optimize("O3")

#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <string>
#include <assert.h>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <random>
#include <cmath>

#define ll long long
#define ld long double
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define int long long
using namespace std;

const int INF = 2e9;
const int MOD = 1e9 + 7;
const int MB = 20;

void solve() {
	int n;
	cin >> n;
	vector<int> a(n);
	int sum = 0;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		sum += a[i];
	}
	int x = sum / n;
	int count = sum % n;
	sort(a.rbegin(), a.rend());
	int ans = 0;
	for (int i = 0; i < n; i++) {
		if (a[i] > x) {
			if (count > 0) ans += (a[i] - x - 1);
			else ans += (a[i] - x);
			count--;
		}
	}
	cout << ans;
}

signed main() {
	/*
	freopen("search.in", "r", stdin);
	freopen("search.out", "w", stdout);
	*/
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cout.precision(12);
	solve();
}

D. Gadgets for dollars and pounds

思路

显然, 如果天数越多, 我们买完的可能性就越大, 所以是满足单调的。 我们可以二分,二分能否在k天买完。
这样的话, 我们可以找前k天最小的汇率, 然后把每个物品乘以当天汇率转换成卢布, 并按照价钱排序, 只需判断用s卢布是否能买完前k个物品的价钱和即可。

代码

#include<bits/stdc++.h>

using namespace std;

int n, m, k ,s;
int a[200010];
int b[200010];
typedef long long ll;
struct object
{
	int type;
	int v;
}ob[200010];
struct haha
{
	long long v = 0;
	int id;
}now[200010];
bool cmp(haha x, haha y)
{
	return x.v < y.v;
}
int pnow;
int ansa, ansb;
bool check(int day)
{
	pnow = 0;
	 ansa = 0;
	 ansb = 0;
	int min_a = 1000010, min_b = 1000010;
	for(int i = 1; i <= day; i++)
	{
		if(min_a > a[i])
		ansa = i;
		min_a = min(min_a, a[i]);
		if(min_b > b[i])
		ansb = i;
		min_b = min(min_b, b[i]);
	}
	//cout << min_a << " " << min_b << endl;
	for(int i = 1; i <= m; i++)
	{
		if(ob[i].type == 1)
		{
			now[++pnow].v = (ll)ob[i].v * min_a;
			now[pnow].id = i;
		}
		else
		{
			now[++pnow].v = (ll)ob[i].v * min_b;
			now[pnow].id = i;
		}
	}
	sort(now+1, now+pnow+1, cmp);
	/*for(int i = 1; i <= pnow; i++)
	{
		cout << now[i] << " ";
	}
	cout << endl;*/
	long long sum = 0;
	for(int i = 1; i <= k; i++)
	{
		sum += now[i].v;
	}
	if(sum <= s)
	{
		return true;
	}
	else
	{
		return false;
	}
}
int main()
{
	cin >> n >> m >> k >> s;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	for(int i = 1; i <= n; i++)
	{
		cin >> b[i];
	}
	for(int i = 1; i <= m; i++)
	{
		cin >> ob[i].type >> ob[i].v;
	}
	int l = 1;
	int r = n;
	int ans = -1;
	while(l <= r)
	{
		int mid = (l + r) >> 1;
		//cout << l << " " << mid << " " << r << endl; 
		if(check(mid))
		{
			ans = mid;
			r = mid - 1;
		}
		else
		{
			l = mid + 1;
		}
	}
	cout << ans << endl;
	if(ans == -1)
	{
		return 0;
	}
	check(ans);
	for(int i = 1; i <= k; i++)
	{
		int p = now[i].id;
		if(ob[p].type == 1)
		{
			cout << p << " " << ansa << endl;
		}
		else
		{
			cout << p << " " << ansb << endl;
		}
	}
	return 0;
}

大佬思路相同, 只不过开了个pair方便输出罢了


#pragma GCC optimize("O3")

#define _CRT_SECURE_NO_WARNINGS
#define _USE_MATH_DEFINES
#include <iostream>
#include <vector>
#include <algorithm>
#include <stack>
#include <string>
#include <assert.h>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <random>
#include <cmath>

#define ll long long
#define ld long double
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define int long long
using namespace std;

const int INF = 2e9;
const int MOD = 1e9 + 7;
const int MB = 20;

bool check(int mid, vector<int>& a, vector<int>& b, vector<pair<int, int>>& c, int k, int s) {
	int mn1 = INF;
	int mn2 = INF;
	for (int i = 0; i <= mid; i++) mn1 = min(mn1, a[i]);
	for (int i = 0; i <= mid; i++) mn2 = min(mn2, b[i]);
	vector<int> w;
	for (int i = 0; i < c.size(); i++) {
		if (c[i].first == 1) w.pb(mn1 * c[i].second);
		else w.pb(mn2 * c[i].second);
	}
	sort(w.begin(), w.end());
	for (int i = 0; i < w.size(); i++) {
		if (w[i] > s) return false;
		s -= w[i];
		if (i + 1 == k) return true;
	}
}

void solve() {
	int n, m, k, s;
	cin >> n >> m >> k >> s;
	vector<int> a(n), b(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	for (int i = 0; i < n; i++) {
		cin >> b[i];
	}

	vector<pair<int, int>> c(m);
	for (int i = 0; i < m; i++) {
		cin >> c[i].first >> c[i].second;
	}

	int l = -1;
	int r = n;
	while (r - l > 1) {
		int mid = (r + l) >> 1;
		if (check(mid, a, b, c, k, s)) r = mid;
		else l = mid;
	}
	if (r == n) {
		cout << -1;
		return;
	}
	
	int mn1 = INF;
	int mn2 = INF;
	int num1 = 0;
	int num2 = 0;
	for (int i = 0; i <= r; i++) {
		if (mn1 > a[i]) {
			mn1 = a[i];
			num1 = i + 1;
		}
	}
	for (int i = 0; i <= r; i++) {
		if (mn2 > b[i]) {
			mn2 = b[i];
			num2 = i + 1;
		}
	}

	vector<pair<int, pair<int, int>>> w;
	for (int i = 0; i < c.size(); i++) {
		if (c[i].first == 1) w.pb({ mn1 * c[i].second, {i + 1, 1} });
		else w.pb({ mn2 * c[i].second, {i + 1, 2} });
	}
	sort(w.begin(), w.end());
	vector<pair<int, int>> ans;
	for (int i = 0; i < k; i++) {
		if (w[i].second.second == 1) {
			ans.pb({ w[i].second.first, num1 });
		}
		else {
			ans.pb({ w[i].second.first, num2 });
		}
	}

	cout << r + 1 << '\n';
	for (auto& p : ans) {
		cout << p.first << " " << p.second << '\n';
	}
}

signed main() {
	/*
	freopen("search.in", "r", stdin);
	freopen("search.out", "w", stdout);
	*/
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cout.precision(12);
	solve();
}

E. Minimum spanning tree for each edge

这题我WA了, 但我知道思路
这个题和非严格最小生成树很像
首先,如果i这条边在最小生成树里面的话, 直接输出最小生成树的权值即可。
否则, 我们考虑这个边的端点u,v。如果我们想加入一条边, 那么必然要删去另一条边。(树只有n-1条边)
能不能乱删呢。

显然是不能的。

中间省略若干条边
中间省略若干条边
u
v
lca
root
uson
vson

如果我们断掉其中一条边而添边(u, v), 那么显然 只能删u, v,lca这个环上得边,否则就会形成环。
那这么多边删哪条呢?因为要求最小, 所以删这个环上除(u,v)外最大的边即可。
实现:先用kruscal求出最小生成树, 在枚举每条非树边, 求他们的lca, 并维护一个点到它lca上路径的最大值 (倍增)。那么,答案就是原最小生成树减去u,v到lca的最大边并加上(u, v)即可。

来一发 题解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值