暑假集训_复健杂题
A Getting Zero
题意
给一个数字 a ,可以进行如下两个操作。
a=(a+1 )%32768
a=(a*2 )%32768
问最多进行几次操作可以使a=0.
思路
易得 ,因此对于一个数最多进行15次操作,可以暴力求解,为了使一个数尽量变大因先加在乘。
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e4 + 233; int pj[20]; const int mod = 32768; void solve() { int x; cin >> x; int cnt = 15; for (int i = 0; i <= 15; i++) for (int j = 0; j <= 15; j++) if ((x + i) * pj[j] % mod == 0) cnt = min(cnt, i + j); cout << cnt << endl; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); pj[0] = 1; for (int i = 1; i <= 15; i++) pj[i] = pj[i - 1] * 2; int t; cin >> t; while (t--) solve(); return 0; }
B Increase Subarray Sums
题意
给定一个序列 {} 与整数 x
定义 f(k) 表示经过如下操作后, 序列 a 中最大的连续子段和: 将 a 中 k 个不同的位置上的数加上 x
请求出 f(k), k∈[0,n]
思路
先求出区间长度为分别为0~n时的最大的连续子段和,再加上k个x时枚举求出最大数。
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e9; int s[5010], f[5010]; void solve() { int n, x; cin >> n >> x; for (int i = 1; i <= n; i++) { int a; cin >> a; s[i] = s[i - 1] + a; } for (int i = 0; i <= n; i++) f[i] = -N; f[0]=0; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) f[i] = max(f[i], s[j] - s[j - i]); for (int i = 0; i <= n; i++) { int ans = -N; for (int j = 0; j <= n; j++) ans = max(ans, f[j] + min(i, j) * x); cout << ans << " "; } cout << endl; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; }
C Integral Array
题意
给定一个数组 a , 我们称该数组完整需要满足 :若数组a 中存在两数 x,y , 使 y≤x (x,y 可以是同一个数) , 则⌊x/y⌋也必须在数组 a 中 , 现需要判断数组a 是否完整 。
思路
这样,我们先找到一个在数组出现过的 i,再找到一个没有在数组里出现过的 j,因为 j 不存在,所以 i*j 是一定不会出现在一个完整的数组里的。如果i*j出现在数组里了,那么说明这个数组一定不是完整的。
首先开一个 cnt[] 数组记录每个数出现的个数,然后对 cnt 数组进行前缀和(前缀和在判断数是否出现时有妙用)。
然后双重循环开始枚举i和j,需要判断cnt[i] > 0 和 cnt[j] == 0,如果前缀和 sum_cnt[i * j - 1] !=sum_cnt[i * (j + 1) - 1] 则说明在 i * j ~ i * (j + 1) - 1 之间有数存在,说明这个数组一定不是好的。
解释一下这里前缀和的使用:
如果sum[1] == sum[10] 说明在[1,10]区间内没有数,两者才会相等
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e5, mod = 1e9 + 7; ll all[N]; ll sum_cnt[N]; // 前缀和cnt ll cnt[N]; void solve() { int n, c; cin >> n >> c; for (int i = 0; i <= 2 * c; i++) cnt[i] = 0; // 初始化cnt for (int i = 1; i <= n; i++) cin >> all[i], cnt[all[i]]++; for (int i = 1; i <= 2 * c; i++) sum_cnt[i] = sum_cnt[i - 1] + cnt[i]; ll x = 0, ok = 1; for (int i = 1; i <= c; i++) { if (cnt[i] > 0) { for (int j = 1; i * j <= c; j++) { if (cnt[j] == 0) { if (sum_cnt[i * j - 1] != sum_cnt[i * (j + 1) - 1]) { ok = 0; break; } } } } } if (ok) cout << "Yes" << endl; else cout << "No" << endl; } int main() { ios::sync_with_stdio(false); int t; cin >> t; while (t--) solve(); return 0; }
D Water the Trees
题意
有一个长 n 的数列 h1,h2,…,hn,对于每次操作,你有两种方法:
- 选择一个数,如果这是第奇数次操作,给它加 1,否则,给它加 2。
- 什么也不做(但也算一次操作)。
注:每次操作最多只能选一个数。
求至少要经过多少次操作,使得数列中所有数均相等。
思路
我们发现,只有两种情况,奇数次和偶数次。那么,可以贪心来考虑。
可以发现,设定一个目标高度,显然每棵树有两种情况,要么差是偶数,要么差是奇数。
这时,偶数我们就可以在偶数次操作,奇数可以在奇数次操作。但最后肯定会剩余某些树,要么是全差奇数次,要么是全差偶数次。
所以说,如果需要 +2 的次数多,就尽可能把 +2 分给 +1。
我们可以 3个 +2 为一组,为什么呢?可以发现,等于 2 个 +1 和 2 个 +2,这时再去贪心求解,答案就出来了。
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e6; ll s[N], n; ll solve(int x) { ll a = 0, b = 0; ll ans = 0; for (int i = 1; i <= n; i++) { a += (x - s[i]) % 2; b += (x - s[i]) / 2; } if (b - a > 1) { ll x = b - a + 1; a += (x / 3) * 2; b -= x / 3; } if (a > b) return a * 2 - 1; else return b * 2; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while (t--) { cin >> n; ll x = -N; for (int i = 1; i <= n; i++) { cin >> s[i]; x = max(x, s[i]); } cout << min(solve(x), solve(x + 1)) << endl; } return 0; }
E Make it Increasing
题意
给定一个包含 n 个正整数的数列 a 以及一个长度为 n 的数列 b ,初始时数列 b 的每一个元素都为0。
定义一次操作为把数列 b 中的某个元素 bi 加上或减去 ai 的值,求使得数列 b 严格递增最小的操作次数。
思路
数据范围比较小可以暴力求解,完成操作后的数组 b 可以看成三部分。
负数,0,正数。
枚举 0 的位置,向前和向后遍历,将 ai 乘上一个数使它刚好比上一个 bi+1 小(或者比 bi-1 大)。
最后输出操作数量的最小值即可。
代码
#include <bits/stdc++.h> using namespace std; #define endl "\n" typedef long long ll; const ll N = 1e18; void solve() { int n; cin >> n; ll s[n + 10]; ll ans = N; for (int i = 1; i <= n; i++) cin >> s[i]; for (int i = 2; i < n; i++) { ll a = 0, b = 0; ll cnt = 0; for (int j = i - 1; j >= 1; j--) { if (s[j] > cnt) { a++; cnt = s[j]; } else { ll k = cnt / s[j] + 1; cnt = s[j] * k; a = a + k; } } cnt = 0; for (int j = i + 1; j <= n; j++) { if (s[j] > cnt) { b++; cnt = s[j]; } else { ll k = cnt / s[j] + 1; cnt = s[j] * k; b = b + k; } } ans = min(ans, a + b); } cout << ans; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); // int t; // cin >> t; // while (t--) solve(); return 0; }