Codeforces Round #702 (Div. 3) 题解
写在前边
链接:Codeforces Round #702 (Div. 3)
比较简单,但是总是感觉脑子有点转不过弯来。
A. Dense Array
链接:A题链接
题目大意:
在数组中插入若干个数,使得,问至少需要插入多少个数。
思路:
既然是相邻,那么只需要顺序模拟即可,每次把, 直到它的两倍大于等于停止,求累计次数。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 55;
void solve() {
int n;
int a[N];
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
int res = 0;
for (int i = 1; i < n; i++) {
if (max(a[i], a[i - 1]) > 2 * min(a[i], a[i - 1])) {
int m = min(a[i], a[i - 1]);
while (m * 2 < max(a[i], a[i - 1])) {
res++;
m *= 2;
}
}
}
printf("%d\n", res);
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
B. Balanced Remainders
链接:B题链接
题目大意:
给定一个有(n可以被3整除)个元素的数组,分别表示数组中元素模后的余数为的数的个数,现在每次操作可以使一个数加,问最少经过几次操作可以使得
思路:
可以发现,之间可以进行单一方向的转移,例如模余数为的数加后余数变为,余数为的数加后余数变为,余数为的数加后余数变为
,于是问题就转化成了转移多少次的问题,相邻的转移又一定是最优的。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
#include <unordered_map>
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 3e4 + 10;
int a[N];
void solve() {
int n;
scanf("%d", &n);
unordered_map<int, int> hash;
int cnt0 = 0, cnt1 = 0, cnt2 = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &a[i]);
hash[a[i] % 3]++;
}
int aver = n / 3;
int res = 0;
while (1) {
if (hash[1] == hash[2] && hash[1] == hash[0] && hash[0] == hash[2]) {
break;
}
if (hash[1] < aver) {
hash[0] -= (aver - hash[1]);
res += (aver - hash[1]);
hash[1] = aver;
}
if (hash[2] < aver) {
hash[1] -= (aver - hash[2]);
res += (aver - hash[2]);
hash[2] = aver;
}
if (hash[0] < aver) {
hash[2] -= (aver - hash[0]);
res += (aver - hash[0]);
hash[0] = aver;
}
}
printf("%d\n", res);
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
C. Sum of Cubes
链接:C题链接
题目大意:
判断一个数是否满足
思路:
因为数据最大为,所以,所以一种做法是预处理出,然后再枚举,查找。
另一种方法是枚举,二分,复杂度
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
void solve() {
LL x;
scanf("%lld", &x);
LL t1;
for (LL i = 1; i <= 10000; i++) {
LL l = 1, r = 10000;
while (l < r) {
LL mid = l + r >> 1;
LL sum = i * i * i + mid * mid * mid;
if (sum > x) r = mid;
else if (sum < x) l = mid + 1;
else {
puts("YES");
return;
}
}
}
puts("NO");
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
D. Permutation Transformation
链接:D题链接
题目大意:
给定一个数组,用其数据建一棵树,最大的数始终作为树根,树根左侧数构成左子树,右边的数构成右子树,同理选大的作为子树的根,求每个数在树中所处的深度。
思路:
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 110;
int a[N];
int res[N];
int n;
void build(int l, int r, int depth) {
if (l > r) return;
int idx = l;
for (int i = l; i <= r; i++) {
if (a[i] > a[idx]) idx = i;
}
res[idx] = depth;
build(l, idx - 1, depth + 1);
build(idx + 1, r, depth + 1);
}
void solve() {
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
build(0, n - 1, 0);
for (int i = 0; i < n; i++) printf("%d ", res[i]);
puts("");
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
E. Accidental Victory
链接:E题链接
题目大意:
个玩家,每人都有一个点数,两个人对战,点数大的获胜,相同点数随机一人获胜,获胜后获得败者的点数,问有哪些人会有获胜的可能性。
思路:
一个人要想获胜,那么肯定需要干掉所有的人才可以,首先按照点数给人排序,我们发现对于第i个人它可以直接获得它之前人的所有点数,这里可以用前缀和处理,如果可以赢得比赛,那么他后边的人也一定能赢得比赛,因为他后边的人可以获得更多的点数,所以可以发现有二段性,因此我们可以直接二分出第一个获胜的人,剩下的直接输出即可。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
#define score first
#define num second
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 2e5 + 10;
LL b[N];
int n;
PII a[N];
bool check(int x) {
LL t = b[x];
for (int i = x + 1; i <= n; i++) {
if (t >= a[i].score) {
t += (LL) a[i].score;
continue;
}
else return false;
}
return true;
}
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].score);
a[i].second = i;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) b[i] = b[i - 1] + (LL) a[i].score;
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
vector<int> res;
for (int i = l; i <= n; i++) res.push_back(a[i].num);
sort(res.begin(), res.end());
printf("%d\n", n - l + 1);
for (auto &it : res) printf("%d ", it);
puts("");
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
F. Equalize the Array
链接:F题链接
题目大意:
设为数组中每个数的出现次数,如果一个数组中的数只有次或者次可以认为这个数组是漂亮的,所以我们现在要做的就是删掉某些数使得这个数组是漂亮的。
思路:
按照题意,小于的那么全部删除,大于的需要删除个,所以有:
看到这个公式能想到什么,当然是前缀和。
但是有一个问题,的范围一定是某个数的出现次数吗,如果不是,那么就没法用前缀和做了,那怎么证明呢,如果不是某个数的出现次数,我们令某个刚好大于的次数为,可以发现,没有发生变化,而大于,这样于是删除个数变大了,所以我们根本不需要考虑如果不是某个数的出现次数这种情况。
有了前缀和,根据公式,左边就有, 右边就有,于是更新答案即可。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 2e5 + 10;
int n, c;
LL presum[N];
bool cmp(int a, int b) {
return a > b;
}
void solve() {
scanf("%d", &n);
map<LL, LL> mp;
for (int i = 0; i < n; i++) {
scanf("%d", &c);
mp[c]++;
}
vector<LL> cnt; //计数 数字出现的次数
for (auto &it : mp) cnt.push_back(it.second);
cnt.push_back(0);
sort(cnt.begin(), cnt.end());
LL res = n;
int last = cnt.size() - 1;
for (int i = 1; i < cnt.size(); i++) presum[i] = presum[i - 1] + cnt[i];
for (int i = 1; i <= last; i++) {
LL left = presum[i - 1], right = (presum[last] - presum[i - 1]) - (last - i + 1) * cnt[i];
res = min(res, left + right);
}
printf("%d\n", res);
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
G - Old Floppy Drive
链接:G题链接
题目大意:
现在有个含有个元素的数组,且有一个指针指向的初始,还有一个个元素的数组,对于每一个,指针会不停的运动,如果数组末尾那么它会重新指会第一个元素继续运动,直到指针走过的数组所有元素的和,那么指针停止,求指针的运动次数。
思路:
设数组总和为,前缀和为,运行一次的时间是,那么运行秒,那么走过的总和:
由此可见,如果 并且 那么指针将永远运行下去不会停止,那么直接输出即可。
然后就分别求磁盘转动的整圈数,然后再加上的最小位置,可以知道,对于磁盘的转动整圈数不得少于,少于转不到,多于的话也就不能保证答案是最小位置了。
最后就需要找到的位置了,可以用二分,注意这个前缀和并不是随随便便的前缀和,处理它的时候保证它是单调递增的且,如果小于等于那么对总和没有任何贡献,并且单调递增才能保证我们可以得到那个刚好的位置,而这一步我们可以用二分lower_bound
寻找即可。
代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 2e5 + 10;
int n, m;
LL presum[N];
LL preInd[N]; //记录前缀的坐标
void solve() {
int idx = 0; //当前坐标
LL allsum = 0, c;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &c);
allsum += c;
if (allsum > presum[idx]) {
presum[++idx] = allsum;
preInd[idx] = i;
}
}
for (int i = 1; i <= m; i++) {
LL x;
scanf("%lld", &x);
if (presum[idx] < x && allsum <= 0) {
printf("%d ", -1);
continue;
}
LL needspins = 0;
if (presum[idx] < x) {
needspins = (x - presum[idx] + allsum - 1) / allsum;
}
x -= needspins * allsum;
LL q = lower_bound(presum + 1, presum + idx + 1, x) - presum;
LL res = needspins * n + preInd[q] - 1; //因为前缀从1开始,那么减去1
printf("%lld ", res);
}
puts("");
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端