A - Dense Array
定义一个数组是好的当且仅当相邻两个数较大的除以较小的<=2时成立。问给定一个数组,最少需要插入多少个数可以 s.t. 数组变成好的。
Solution
直接按照要求模拟
Code
#include<bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; int arr[maxn]; int cal(int x, int y) { if (x > y) swap(x, y); int ans = 0; while(true) { if (x * 2 >= y) break; x <<= 1; ans++; } return ans; } void solve() { int n; scanf("%d", &n); int ans = 0; for (int i = 1; i <= n; ++ i) { scanf("%d", &arr[i]); if(i > 1) { ans += cal(arr[i - 1], arr[i]); } } printf("%d ", ans); } int main() { int t = 1; scanf("%d", &t); while(t--) { solve(); } }
B - Balanced Remainders
给定一个数组,所有值取模3。每次可以给一个数+1,算一次操作。问最少多少次操作可以 s.t. 取模后数组中0 1 2的数量一致
Solution
想象成一个环状的推箱子。最多推两次(猜的,但我写的是while(true))就可以全部推平了
Code
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn = 3e4 + 10; int arr[maxn]; void solve() { int n; scanf("%d", &n); int a[5]; memset(a, 0, sizeof a); for (int i = 1; i <= n; ++ i) { scanf("%d", &arr[i]); a[arr[i] % 3]++; } int cnt = n / 3; if(a[0] == cnt && a[1] == cnt && a[2] == cnt) { printf("0 "); return; } int xx = 0; int ans = 0; while(true) { bool flag = false; for (int i = 0; i < 3; ++ i) { if(a[i] > cnt) { flag = true; a[(i + 1) % 3] += a[i] - cnt; ans += a[i] - cnt; a[i] = cnt; } } if(!flag) break; } printf("%d ", ans); } int main() { int t = 1; scanf("%d", &t); while(t --) { solve(); } return 0; }
C - Sum of Cubes
问一个数可否由两个正整数的三次方相加得到。
Solution
看到数据范围。枚举 i 即可,判断是否存在 j 即可。
Code
#include<bits/stdc++.h> using namespace std; #define ll long long constexpr int maxn = 1e5 + 10; bool check(ll xx) { ll xll = 1, xrr = min(xx,10000*1ll); ll ans = -1; while(xll <= xrr) { ll mid = xll + xrr >> 1; if (mid * mid * mid < xx) { xll = mid + 1; } else { xrr = mid - 1; ans = mid; } } return ans * ans * ans == xx; } void solve() { ll n; scanf("%lld", &n); for (ll i = 1; i <= n; ++ i) { if (i * i * i > n) { break; } ll xxxx = n - i * i * i; if(check(xxxx)) { printf("YES "); return; } } printf("NO "); return; } int main() { int t = 1; scanf("%d", &t); while(t --) { solve(); } return 0; }
D - Pythagorean Triples
给定一个全排列,要求转换成二叉树。选当前序列最大值作为根,左侧最大值作为左子树,右侧最大值作为右子树。递归。
Solution
生成方法是递归,做法也是。
Code
#include<bits/stdc++.h> using namespace std; const int maxn = 1e2 + 10; int dep[maxn], arr[maxn]; void dfs(int ll, int rr, int dd) { if(ll > rr) return; int MAX = -1; int pos = -1; for (int i = ll; i <= rr; ++ i) { if (MAX < arr[i]) { MAX = arr[i]; pos = i; } } dep[pos] = dd; dfs(ll, pos - 1, dd + 1); dfs(pos + 1, rr, dd + 1); } void solve() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++ i) { scanf("%d", &arr[i]); } dfs(1, n, 0); for (int i = 1; i <= n; ++ i) { printf("%d%c", dep[i], " "[i == n]); } } int main() { int t = 1; scanf("%d", &t); while(t --) { solve(); } return 0; }
E - Accidental Victory
有n个人。每个人有初始分数ai,一共会发生n-1(每个人都会打2场比赛)场比赛。如果分数相同那么会随机判定一个人获胜。获胜方可以获得败方的所有分数。最后还有分数的人获胜。问有哪些人可能获胜。
Solution
分数从小到大排序,做个前缀和。如果当前前缀和<下一个位置的分数,那么从当前位置向前所有的人都不可能获胜。找到最后这样一个位置即可。
Code
#include<bits/stdc++.h> using namespace std; #define int long long const int maxn = 2e5 + 10; int arr[maxn], nums[maxn]; struct PERSON { int id, mon, xx; } person[maxn]; bool cmp(PERSON x, PERSON y) { return x.mon < y.mon; } void solve() { int n; scanf("%lld", &n); for (int i = 1; i <= n; ++ i) { scanf("%lld", &person[i].mon); person[i].id = i; person[i].xx = 1; } sort(person + 1, person + 1 + n, cmp); set<int> ans; int xxx = 0; int last = 1; for (int i = 1; i <= n; ++ i) { if(i != 1) { if(xxx < person[i].mon) { last = i; } } xxx += person[i].mon; } for (int i = last; i <= n; ++i) ans.insert(person[i].id); printf("%lld ", (int)ans.size()); for(auto xxx : ans) printf("%lld ", xxx); puts(""); } signed main() { int t = 1; scanf("%d", &t); while(t --) { solve(); } }
F - Equalize the Array
定义一个数组是漂亮的当且仅当数组中所有相同的数出现的次数等于一个常数C或者等于0。问最少删掉多少个数才能 s.t. 数组是一个漂亮的数组。
Solution
离散化之后枚举最终结果数组的常数C,然后统计需要删除的数取min即可。
Code
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; int arr[maxn], brr[maxn]; void solve() { int n; scanf("%d", &n); for (int i = 1; i <= n; ++ i) scanf("%d", &arr[i]); sort(arr + 1, arr + 1 + n); int index = 0; for (int i = 1; i <= n; ++ i) { if(i == 1) { brr[++index] = 1; } else { if(arr[i] != arr[i-1]) brr[++index] = 1; else brr[index]++; } } int MIN = INT_MAX; sort(brr + 1, brr + 1 + index); for (int i = 1; i <= index; ++ i) { MIN = min(MIN, n - brr[i] * (index - i + 1)); } printf("%d ", MIN); } int main() { int t = 1; scanf("%d", &t); while(t --) { solve(); } return 0; }
G - Old Floppy Drive
给定一个循环数组和X,从第一位开始累加,直到Sum >= X为止,问需要累加多少次或者永远不能达到
Solution
pref数组定义成前缀和数组。总和为Sum。Max代表pref中的最大值
如果 X > MAX && Sum <= 0,显然无解
其他情况都有解。 对于给定 X,总共需要跑 t1 = (X - Max + Sum - 1)/ Sum次,带来的贡献等于 t2 = t1 * Sum
那么我们还需要至少有 t3 = X - t2的贡献。
显然如果 pref[i-1] >= pref[i],那么pref[i]就是一个无效点,我们就可以维护出一个新的前缀和数组pref',这个数组里的值是严格递增的,这样我们就可以二分找到第一个大于等于t3的贡献点以及对应位置aa。
于是我们得出答案为 t1 * n + aa。
Code
#include<bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; #define ll long long void solve() { vector<ll> pref; vector<int> ind; int n, m; ll Sum = 0; scanf("%d %d", &n, &m); ll MAX = -1; ll temp; for (int i = 1; i <= n; ++ i) { scanf("%lld", &temp); Sum += temp; if(pref.empty() || Sum > pref.back()) { pref.push_back(Sum); ind.push_back(i-1); } } while (m--) { ll x; scanf("%lld", &x); if (x > pref.back() && Sum <= 0) { printf("-1 "); continue; } ll ans = 0; if (pref.back() < x) { ans = (x - pref.back() + Sum - 1) / Sum; } x -= ans * Sum; printf("%lld ", ans * n + ind[lower_bound(pref.begin(), pref.end(), x) - pref.begin()]); } puts(""); } int main() { int t = 1; scanf("%d", &t); while(t --) { solve(); } return 0; }