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