代佬们关注关注我吧(卑微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;
}