A. Most Unstable Array
\(Description:\)
给定 \(n,\ m\),构造出一个长度为 \(n\) 的数组 \(a\),使得数组的和为 \(m\),在此条件下,\(\sum_{i=1}^{n - 1}\left | a_i - a_{i + 1} \right |\) 最大是多少?
\(Solution:\)
显然当 \(n \geq 3\) 的情况下,构造 \(0,\ m,\ 0,\ 0,\ ...\) 这样的最优,再特判 \(n \leq 2\) 的情况即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
int main(){
int t; cin >> t;
while(t --){
int n, m; cin >> n >> m;
if(n == 1)cout << 0 << endl;
else if(n == 2) cout << m << endl;
else cout << m * 2 << endl;
}
return 0;
}
B. Two Arrays And Swaps
\(Description:\)
给定两个数组,你可以交换 \(k\) 次 两个数组的元素,问最后 \(a\) 数组的和最大可以是多少?
\(Solution:\)
将两个数组排序后,贪心的交换即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 40;
int a[N], b[N];
int main(){
int t; cin >> t;
while(t --){
int n, k;
cin >> n >> k;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) cin >> b[i];
sort(a + 1, a + n + 1);
sort(b + 1, b + n + 1);
int sum = 0;
for(int i = 1; i <= n; i ++) sum += a[i];
int idx = n;
for(int i = 1; i <= n; i ++){
if(a[i] < b[idx] && k){
k --;
sum += b[idx --] - a[i];
}else break;
}
cout << sum << endl;
}
return 0;
}
C. Board Moves
\(Description:\)
有一个 \(n \times n\) 的矩阵(\(n\) 为奇数),每个矩阵单元有一个物品,每次操作你可将一个单元里的一个物品移动到该单元周围的八个单元里,问最后只有一个单元有物品的情况下,最少要多少次操作?
\(Solution:\)
我们可以将所有物品最终都汇聚到矩阵的中心的单元格,那么以此为中心,他周围的第一圈的单元格的物品只需要一步就可以,第二圈就需要两步,以此类推。
那么我们可以预处理出每一圈的单元格的数量,乘以他对应的步数,再求和即可。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
typedef long long ll;
int f[N];
int main(){
f[0] = 0;
for(int i = 1; i < N; i ++){
f[i] = f[i - 1] + 8;
}
int t; cin >> t;
while(t --){
ll n; cin >> n;
n = n * n - 1;
ll ans = 0;
for(int i = 1; ; i ++){
if(n >= f[i]){
n -= f[i];
ans += 1ll * i * f[i];
}else break;
}
cout << ans << endl;
}
return 0;
}
D. Constructing the Array
\(Description:\)
有长度为 \(n\) 的数组 \(a\) ,全为 \(0\),接下来循环 \(n\) 次,每次选出一段最长的连续区间 \([l,\ r]\)(全为 \(0\) ,如果一样长,就选最左边的)。
如果 \(r - l + 1\) 是奇数,那么 \(a[\frac{l + r}{2}] = i\);
否则,\(a[\frac{l + r - 1}{2}] = i\);(\(i\) 是第几轮循环)。
输出最终的数组 \(a\)。
\(Solution:\)
我们可以直接暴力去做。即我们每次选出符合条件的 \([l,\ r]\),然后对应的给 \(a[i]\) 赋值,又得到了两个新的更小的区间,我们需要存储下来,并按照上述的规则对所有的区间排序。显然优先队列可以完美的满足我们的要求。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct node{
int l, r;
friend bool operator < (const node &a, const node &b){
if(a.r - a.l == b.r - b.l) return a.l > b.l;
return a.r - a.l < b.r - b.l;
}
};
int a[N];
int main(){
int t; cin >> t;
while(t --){
int n; cin >> n;
priority_queue<node> q;
q.push({1, n});
int idx = 1;
while(!q.empty()){
node cur = q.top(); q.pop();
int l = cur.l, r = cur.r;
int mid;
if((r - l + 1) & 1) mid = l + r >> 1;
else mid = l + r - 1 >> 1;
a[mid] = idx ++;
if(mid - l >= 1) q.push({l, mid - 1});
if(r - mid >= 1) q.push({mid + 1, r});
}
for(int i = 1; i <= n; i ++) printf("%d ", a[i]);
puts("");
}
return 0;
}
E. K-periodic Garland
\(Description:\)
给定长为 \(n\) 的 \(0,\ 1\) 字符串,你可以通过一次操作改变一个字符(\(0\) 变 \(1\) \(or\) \(1\) 变 \(0\)),问最少几次操作可以使任意相邻两个 \(1\) 之间的距离为 \(k\) ?
\(Solution:\)
显然根据题意可以得知最终所有 \(1\) 的位置 \(mod\ k\) 的余数 \(t\) 都相同。那么我们就可以去枚举 \(t\),判断在这种情况下,需要几次操作,最后取 \(min\) 即可。
首先我们要知道的是我们最多改变多少次:原字符串 \(1\) 的个数 \(- 1\) 次。
我们先算出原字符串 \(1\) 的个数 \(cnt\)。然后假设原字符串的 \(1\) 对应 \(1\),\(0\) 对应 \(-1\),那么我们将对应位即:\(t,\ t + k,\ t + 2 * k,\ ...\) 的字符提取出来,按照对应方式组成一个新序列,那么求出该序列的最大连续子段和 \(cur\),那么 \(min(cnt - cur)\) 就是答案。
我们知道只有 \(t,\ t + k,\ ...\) 这些位才有可能为 \(1\),那么我们求出的 \(cur\) 无非就两种情况:
\(1.\) \(a,\ b,\ c\) (\(a,\ c\)是一连串的 \(-1\); \(b\) 是一连串的 \(1\));
\(2.\) \(a,\ b,\ c\) (\(a, c\) 是一连串的 \(1\); \(b\) 是一连串的 \(-1\))(\(a + b + c > max(a, c)\))。
对于 \(1.\) 来说,\(cur\) 个 \(1\) 是不需要改变的,那么在其他的位置多出来 \(cnt - cur\) 个 \(1\),所以需要 \(cnt - cur\) 次来把他们变为 \(0\)。
对于 \(2.\) 来说,有 \(a + c\) 个 \(1\) 是不需要改变的,我们需要把中间的 \(b\) 个 \(0\) 变为 \(1\),还需要把 \(cnt - a - c\) 个其他位置的 \(1\) 变为 \(0\)。即 \(b + cnt - a - c = cnt - cur(cur = a + c + b)\)。
求出最大子段和其实就是最多不需要改变的个数(新序列)。
\(Code:\)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
char s[N];
int main(){
int t; cin >> t;
while(t --){
int n, k;
cin >> n >> k;
scanf("%s", s);
int cnt = 0;
for(int i = 0; i < n; i ++)
cnt += s[i] - '0';
int ans = 0;
for(int i = 0; i < k; i ++){
int cur = 0;
for(int j = i; j < n; j += k){
cur += s[j] == '1' ? 1 : -1;
if(cur < 0) cur = 0;
ans = max(ans, cur);
}
}
cout << cnt - ans << endl;
}
return 0;
}