Educational Codeforces Round 108 (Rated for Div. 2) A~D题解

本文介绍了三道编程竞赛题目,涉及思维与暴力求解策略。RedandBlueBeans问题要求在红豆和蓝豆数量差不超过k的情况下组合成组;TheCakeIsaLie问题探讨在网格中寻找特定费用路径的可能性;BerlandRegional问题要求计算各学校按人数分组的技能值总和。通过分析和代码实现,展示了如何解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代佬们关注关注我吧(卑微55555)



A题:Red and Blue Beans(思维)

题意:给你r个红色的豆和b个蓝色的豆,你可以创造一个组,该组需要满足至少有一个红豆和一个蓝豆,同时两者的数目差不超过k

思路:
1、k为0那么两种豆的数目相等
2、k不为0,那么c代表abs(r-b), 然后我们取出r和b的最小值mi,构造每组先放一个红豆和蓝豆,然后再放最多k个数量比较多的那种颜色的豆,这种组合可以有mi组。如果还有豆多余那么就不行,如果中途分组把豆消耗完了就可以。

#include<iostream>
using namespace std;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int t, r, b, k;
int main(int argc, char const *argv[])
{
	IOS
	cin >> t;
	while(t--)
	{
		cin >> r >> b >> k;
		int judge = 1;
		if (k == 0 && r != b)//必须相等
		{
			judge = 0;
		}
		else if (r != b)
		{
			int mi = r < b?r:b;
			int c = abs(r - b);
			int f;
			if (c / k * k == c)
				f = c / k;
			else
				f = c / k + 1;
			if (f > mi)
				judge = 0;
		}
		if (judge)
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
	}
	return 0;
}

B:The Cake Is a Lie(思维)

题意:给你一个n*m的网格,你在(1,1)点,终点再(n, m),你只能往右或者下走,如果你在(x,y)的位置往右走,那么你要花费x块钱,往下走要花费y块钱,问有没有可能的走法走到(n,m)的时候花费时恰好为k元

思路:说实话,比赛的时候我就试了几种走法,发现花费都是固定的emmmm。这里就不解释为什么了,我相信各位代佬一定懂的!

#include<iostream>
using namespace std;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
typedef long long ll;
const ll mod = 1e9 + 7;
int t, n, m, k;
int main(int argc, char const *argv[])
{
	IOS
	cin >> t;
	int a = 0;
	for (int i = 1; i <= 1000; i++)
	{
		a++;
		a--;
	}
	while(t--)
	{
		cin >> n >> m >> k;
		if (m - 1 + (n - 1) * m == k)
			cout << "YES" << endl;
		else
			cout << "NO" << endl;
 
	}
	return 0;
}

C:Berland Regional(暴力)

我都没想过cf的c会有暴力的题目emmmmm

题意:给定n给人,每个人都有所属的学校和技能值,对于人数为k的分组,每个学校派出自己学校最强的k个人组成1队,次k强组成2队,以此推类,不足k则无法组队,视为多余的人,一个小组的人只能属于同一个学校。
ans【k】为所有学校派出的人数为k的组的所有队员的技能值之和。
要求输出k等于1~n时的所有ans。

思路:(先说明啊,我不知道我的暴力姿势是不是够优雅的哦!)

将每个学生按学校存起来,然后对同一个学校的学生按照技能值做一个降序排序,然后再做一个前缀和, 然后对于k长度的贡献怎么算呢?假定sum【i】为某一个学校的技能值降序后的前缀和,以及该学校有num个人。那么贡献就为
sum[num / k * k],就可以自动忽略后面多余的人啦,然后将贡献加起来输出即可;

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<stack>
#include<vector>
#include<queue>
#include<unordered_map>
#include<stdio.h>
using namespace std;
const int MAXN = 3e5 + 7;
typedef long long ll;
#define INFll 9223372036854775807
#define INF 0x3f3f3f3f
#define dbg(x) cout << #x << " = " << x << endl;
#define lowbit(n) (n&-n)
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
const ll mod = 1e9 + 7;
int t, n, m, k;
int judge[MAXN];
int flag[MAXN];
ll ans[MAXN];
bool cmp(ll x, ll y)
{
	return x > y;
}
vector<int>pos;
vector<ll>vec[MAXN], vec1[MAXN];
int main(int argc, char const *argv[])
{
	IOS
	cin >> t;
	while(t--)
	{
		cin >> n;
		ll x;
		for (int i = 1; i <= n; i++)
		{
			ans[i] = 0;
			cin >> judge[i];//判断这个学生属于哪个学校
			if (flag[judge[i]] == 0)//判断该学校是否出现过,第一次出现那么就记录下来
			{
				flag[judge[i]] = 1;
				pos.push_back(judge[i]);//pos存放出现的学校
			}
		}
		for (int i = 1; i <= n; i++)
		{
			cin >> x;
			vec1[judge[i]].push_back(x);//将学生按学校分组
		}
		for (int i = 0; i < pos.size(); i++)
		{
			int k = pos[i];
			sort(vec1[k].begin(), vec1[k].end(), cmp);//降序排序
			vec[k].push_back(0);//放一个0方便前缀和,懂前缀和的同学应该都知道的吧
			for (int j = 1; j <= vec1[k].size(); j++)
			{
				vec[k].push_back(vec1[k][j - 1] + vec[k][j - 1]);//前缀和
			}
		}

		

		//一定要对于每个学校,分别去判断它对每个k的贡献,这样子对于每个学校就可以有分别一个k的上界
		//本人刚开始傻乎乎的,对于每个k去分别遍历每个学校,一下子就超时了淦
		for (int j = 0; j < pos.size(); j++)
		{
			ll num = vec1[pos[j]].size();
			for (int k = 1; k <= num; k++)
			{
				ans[k] += vec[pos[j]][num / k * k];//存答案

			}
		}
		for (int i = 0; i < pos.size(); i++)//初始化
		{
			int k = pos[i];
			flag[k] = 0;
			vec[k].clear();
			vec1[k].clear();
		}
		pos.clear();
		for (int i = 1; i <= n; i++)
		{
			cout << ans[i] << " ";
		}
		cout << endl;
	}
	return 0;
}

标题D:Maximum Sum of Products(思维+暴力 + 前缀和)

题意:给定两个数组长度为n的数组a和b,对于a数组,你最多可以对它进行一次逆转操作(0或1次),即选择一个a的子序列(连续的序列),然后转置这个子序列,【1 2 3 4 5】,选择【3, 5】后变成【1 2 5 4 3】
val值为a1 * b1 + a2 * b2 + … an * bn,问经过操作后得到的value值最大为多少?(原谅我是新手博主真的不会打公式)

思路:就是暴力,优雅的暴力罢了。模拟逆转的过程,分以下奇偶即可
奇数:遍历每一个位置,然后分别以该位置为中心,往旁边不断的扩展逆转的范围,在扩展的过程中更新答案
偶数:以两个位置为中心点扩张罢了。

不好讲,但是相信各位代佬看代码一定马上看懂了,因为真的很简单易懂!

#include<iostream>
using namespace std;
const int MAXN = 3e5 + 7;
typedef long long ll;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int n;
ll a[MAXN], b[MAXN];
ll sum[MAXN];
ll ans;
int main(int argc, char const *argv[])
{
	IOS
	cin >> n;
	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 <= n; i++)//记录前缀和
	{
		sum[i] = sum[i - 1] + a[i] * b[i];
	}
	ans = sum[n];

	//奇数长度的逆转
	for (int i = 1; i <= n; i++)//i位置为中心
	{
		ll k = a[i] * b[i];//k记录的是逆转这个范围后这个范围内乘积的和
		for (int j = i - 1; j >= 1 && i + (i - j) <= n; j--)//j要满足往左扩张和往右扩张都不越界
		{
			k += a[j] * b[i + i - j] + a[i + i - j] * b[j];//然后将左边的a乘上右边的b,右边的a乘上左边的b
			ans = max(ans, sum[j - 1] + sum[n] - sum[i + i - j] + k);//前缀和去掉中间已经被逆转过的区域
		}
	}

	//偶数同理,不过是中间的是以两个位置为起点扩展罢了
	for (int i = 1; i <= n - 1; i++)
	{
		ll k = a[i + 1] * b[i] + a[i] * b[i + 1];
		ans = max(ans, k + sum[i - 1] + sum[n] - sum[i + 1]);
		for (int j = i - 1; j >= 1 && i + i - j + 1 <= n; j--)
		{
			k += a[j] * b[i + i - j + 1] + a[i + i - j + 1] * b[j];
			ans = max(ans, sum[j - 1] + sum[n] - sum[i + i - j + 1] + k);
		}
	}
	cout << ans << endl;
	return 0;
}

代佬们关注关注我吧(卑微55555)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值