牛客周赛 Round 110 题解
牛客周赛 Round 110
A 小苯的数字染色
手玩发现只有 \(n\) 为 \(1\) 不行。
void solve(){int n;cin >> n;if(n == 1){cout << "NO\n";}else{cout << "YES\n";}
}
B 小苯的数组重排
发现无论怎么排序,\([a_2,a_3,\dots ,a_{n-1}]\) 总是计算了两次,只有 \(a_1\) 和 \(a_n\) 计算了一次,我们只需要将最小的值作为 \(a_1\) 和 \(a_n\) 即可。
可以两次循环找最小值和次小值,时间复杂度为 \(O(n)\);也可直接排序寻找,时间复杂度为 \(O(nlogn)\),题目数据可以通过。
void solve(){int n;cin >> n;vector<int> a(n);ll sum = 0;for(int i = 0;i < n;i++){cin >> a[i];sum += 2 * a[i];}sort(a.begin(), a.end());sum -= a[0] + a[1];cout << sum << "\n";
}
C 小苯的麦克斯
如果能只选一个数,那么只选一个最大值一定是最优解,因为此处 \(MAX\) 最大,\(MEX\) 最小。但题目不允许只选一个数字,那我们考虑选择两个数字(如果最大值固定了多一个数字只会使 \(MEX\) 变大),直接模拟找最大值即可,时间复杂度为 \(O(n)\)。
tip : 其实答案只会在 \(MAX\) 值附近产生,即在含 \(MAX\) 且长度为 \(2\) 的区间中取得。
void solve(){int n;cin >> n;vector<int> a(n);int ans = -100;for(int i = 0;i < n;i++){cin >> a[i];}for(int i = 0;i < n - 1;i++){int mex = 0;if((a[i] == 0 || a[i + 1] == 0) && mex == 0){mex = 1;}if((a[i] == 1 || a[i + 1] == 1) && mex == 1){mex = 2;}int tmp = max(a[i], a[i + 1]) - mex;ans = max(ans, tmp);}cout << ans << "\n";
}
D 小苯的平衡序列
排完序后选择两边的数字分别做一次模拟即可,时间复杂度为 \(O(nlogn)\),主要来自排序。
tip : 也可排完序后对每一个数字都枚举删除,然后确定中位数,用前缀和 \(O(1)\) 计算平衡度,求最值,这个时间复杂度为 \(O(nlogn)\)。
void solve(){int n;cin >> n;vector<int> a(n);for(int i = 0;i < n;i++){cin >> a[i];}sort(a.begin(), a.end());ll ans1 = 0, ans2 = 0;ll med1 = a[n / 2], med2 = a[n / 2 - 1];for(int i = 0;i < n;i++){if(i == 0){ans2 += abs(a[i] - med2);}else if(i == n - 1){ans1 += abs(a[i] - med1);}else{ans1 += abs(a[i] - med1);ans2 += abs(a[i] - med2);}}ll ans = min(ans1, ans2);cout << ans << "\n";
}
E 小苯的数字变换
\(\hspace{15pt}\)小苯在研究一种特殊的数字变换。对于一个正整数 \(x (1 \leq x \leq 10^{1000000}\),保证 \(x\) 不含前导 \(0\)),定义一个数字的"根"为不断将其各位数字相加直到得到个位数。例如:
\(\hspace{15pt}\)\(\hspace{15pt}\)- 根 \((38) = 3+8 = 11 → 1+1 = 2\)
\(\hspace{15pt}\)\(\hspace{15pt}\)- 根 \((999) = 9+9+9 = 27 → 2+7 = 9\)\(\hspace{15pt}\)现在给定一个数字串 \(x\),请你求出:所有 \(x\) 的连续子区间代表的十进制数字(去掉前导 \(0\) 后)的 "根" 之和。
手玩发现:一个数字的 “根” 可以由两两的“根”求“根” / 一个正整数的数根就是其对 \(9\) 取模的值(特殊的,\(9\) 的倍数的根为 \(9\))。
如: \(7963\) 可以先求 \(79\) 的“根”,为 \(7\),然后 \(7\) 与 \(6\) 继续求“根”,然后 \(4\) 与 \(3\) 继续求“根”。
这就可以 \(dp\) 求解,用前面的数递推下一个数字的结果。
void solve(){string s;cin >> s;int n = s.size();s = " " + s;ll sum = 0;vector<vector<int>> dp(n + 1, vector<int>(10, 0));for(int i = 1;i <= n;i++){dp[i][(s[i] - '0')]++;for(int j = 0;j < 10;j++){int t = (j + (s[i] - '0'));if(t >= 10){t = t % 10 + t / 10;}dp[i][t] += dp[i - 1][j];}for(int j = 0;j < 10;j++){sum += dp[i][j] * j;}}cout << sum << "\n";
}
F 小苯的序列合并
\(\hspace{15pt}\)给定长度为 \(n(1 \leq n \leq 3 \times 10^5)\) 的序列 \(a(0 \leq a_i \leq 10^9)\),你可以对 \(a\) 做如下操作任意次:
$\hspace{23pt}\bullet\ $ 选择一个下标 \(i\ (1 \leqq i < |a|)\),将 \(a_i\) 与 \(a_{i+1}\) 合并起来,结果为 \(a_i\oplus a_{i+1}\)。(其中 \(\oplus\) 表示按位异或运算符,\(|a|\) 表示 \(a\) 当前的长度。)
\(\hspace{15pt}\)所有操作结束后,小苯希望你最大化最终 \(a\) 中所有数字的按位与,即 \(\rm AND(\&)\) 值,请你算一下这个最大值是多少吧。
Trick : 这种 \(a_i\) 与 \(a_{i + 1}\) 做运算的式子,如果满足交换律,如 \(\max,\min,\gcd, \text{lcm},+,\times ,\oplus, \&,|\) 等,都可以看作是将 \(a\) 数组进行划分成多块。
结论 : 一定存在一种划分,且划分的块数小于等于 \(2\),使得其结果最大。
证明 : 如果划分的块数 \(\ge 3\),每 \(3\) 个连续的块可继续合并为 \(1\) 块。那原先 \(3\) 个块的与运算结果一定小于或等于异或运算合并后的结果。因为:
-
当与运算结果的某一位为 \(1\) 时,要求原先的 \(3\) 个块的该位都为 \(1\),此时异或运算和它是等价的。
-
当只有 \(1\) 个块的某位为 \(1\) 时,异或运算结果的该位仍然为 \(1\),而与运算结果的该位为 \(0\),此时异或运算结果大于与运算结果。
【三段合并的异或结果是答案的超集,且两段取与操作也一定是答案的超集】
故对于 \(3\) 个及以上的块,使用异或运算合并它们,一定不会更亏。
我们就可以分段枚举分割点,采用前缀异或预处理得到答案,也可用 \(pre\) 和 \(suf\) 数字滚动处理,时间复杂度为 \(O(n)\)。
void solve(){int n;cin >> n;vector<int> a(n);int suf = 0, pre = 0;for(int i = 0;i < n;i++){cin >> a[i];suf ^= a[i];}int ans = suf;for(int i = 0;i < n;i++){pre ^= a[i];suf ^= a[i];ans = max(ans, pre & suf);}cout << ans << "\n";
}
赛时 solve 5 / 6,差一点 pwp,继续加油。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/915986.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!