A Min Max Swap
题意
给定长度为n的数组a和b,可以进行如下任意次数的操作:
选择一个下标 , 交换 和
求经过任意次数的操作后, 的最小值
思路
可以发现a,b中最大的元素对于乘积必然会有贡献,要使得乘积最小,就要使得最大的元素不在另一个数组的最大值尽可能的小
故我们可以选择所有的的,交换二者的值,答案即为交换后的两个数组的最大值相乘
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using ll = long long;
const int N = 110;
int n;
int a[N], b[N];
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
int maxv_a = 0, maxv_b = 0;
for(int i = 1; i <= n; i++) {
if(a[i] > b[i]) swap(a[i], b[i]);
maxv_a = max(maxv_a, a[i]);
maxv_b = max(maxv_b, b[i]);
}
printf("%d\n", maxv_a * maxv_b);
}
int main() {
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}
B. Fun with Even Subarrays
题意
给定一个长度为n的数组,可以进行如下的操作:
从数组中选择一个从l开始,长度为2k的子数组, 对于每一个,令
求使得数组的所有元素相等的最小操作次数
思路
操作实际上就是选择一个长度为2k的子数组,将后一半的数组赋值到前一半的数组中
可以发现数组中的最后一个元素是无法修改成其他元素的,那么数组最后必然全部为最后一个元素的值
我们可以从最后第二个个元素开始,如果当前元素和后面的元素(也就是最后一个元素)不相等的话,令 ,进行一次操作,操作之后, 直到
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int n;
int a[N];
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
int res = 0;
int j = n - 1;
while(true) {
while(j >= 1 && a[j] == a[n]) j--;
if(j < 1) break;
res++;
int len = n - j;
j = j - len;
}
printf("%d\n", res);
}
int main() {
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}
C. And Matching
题意
给定一个n(n为2的整数次幂)以及k
求长度为的数组a和b, a和b中的元素为0,1,2,...n - 1, 每一个数字只能出现一次
并且
思路
假如 n = 8, k = 0, 构造如下:
当时, 只需要将与对应k的所在的位置交换即可
当 时(n = 4时无解), 只需要将的值调换一下即可
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using ll = long long;
const int N = 1e6;
int n, k;
int a[N];
void solve() {
scanf("%d%d", &n, &k);
if(k == n - 1 && n == 4) return puts("-1"), void();
int len = n / 2;
for(int i = 1; i <= n; i++) {
a[i] = i - 1;
}
if(k == n - 1) {
swap(a[1], a[n - 1]);
swap(a[n - 1], a[4]);
} else {
swap(a[1], a[k + 1]);
}
for(int i = 1; i <= len; i++) {
printf("%d %d\n", a[i], a[n - i + 1]);
}
}
int main() {
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}
D. Range and Partition
题意
给定一个长度为n的数组a,找到一个范围[x, y], 将数组a分割成k个连续子数组(), 并且每一个子数组中的元素在[x, y]范围内的数量要严格大于不在[x, y]范围内的数量(在范围内是指)
求最小的y - x, 输出x, y以及分割后的k的数组
思路
- 假设已经给定[x, y],如何判断[x, y]是否满足要求并且找到对应分割的数组
构造数组b,
数组sum为数组b的前缀和数组
每一个子数组中的元素在[x, y]范围内的数量要严格大于不在[x, y]范围内的数量等价于数组a中在[x, y]范围内的数量减去不[在x, y]范围内的数量, 同时也等价于
构造分割的数组:
令f[x]为x第一次在前缀数组sum出现的下标
按照f[1], f[2], ... f[k-1] 进行分割数组即可
- 那么对于每一个x, 可以通过双指针或者二分的方式找到最小的y
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using pii = pair<int, int>;
using ll = long long;
const int N = 2e5 + 10;
int n, k;
int a[N];
void solve() {
scanf("%d%d", &n, &k);
unordered_map<int, int> mp;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
mp[a[i]]++;
}
vector<pii> cnt;
for(auto it : mp) {
cnt.push_back({it.first, it.second});
}
sort(cnt.begin(), cnt.end());
pii res = {1, n + 1};
int sum = 0; // x <= a[i] <= y的数量
for(int i = 0, j = 0; i < cnt.size(); i++) {
while(j < cnt.size() && sum * 2 - n < k) {
sum += cnt[j].second;
j++;
}
// sum * 2 - n = sum - (n - sum) >= k
// 范围内的数量 - 不在范围内的数量 要大于等于 k
if(sum * 2 - n >= k && cnt[j - 1].first - cnt[i].first < res.second - res.first) {
res = {cnt[i].first, cnt[j - 1].first};
}
sum -= cnt[i].second;
}
printf("%d %d\n", res.first, res.second);
int idx = 1;
sum = 0;
for(int i = 1, last = 1; i <= n; i++) {
if(a[i] >= res.first && a[i] <= res.second) sum++;
else sum--;
if(sum == idx) {
if(idx == k) {
printf("%d %d\n", last, n);
break;
} else {
printf("%d %d\n", last, i);
last = i + 1;
idx++;
}
}
}
}
int main() {
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}
E. Paint the Middle
题意
给定长度为n的数组a和c, 开始时所有的, 并且
可以对数组进行如下的操作:
选择三个下标, 然后设置
求进行一系列操作后可能的最大值
思路
只有出现两次以上的数字才可能满足上述操作要求的i和k, 并且出现大于两次的数字来说,选择第一个和最后一个是最优的
使用l[i]和r[i]记录a[i]出现的第一个和最后一个位置
对于每一段[L, R], 能够做出的贡献为max(R - L - 1, 0)
并且如果存在相交的线段, 即则[R, r[a[i]]的贡献为r[a[i]] - RR - 1
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
int n;
int a[N], l[N], r[N];
void solve() {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if(!l[a[i]]) l[a[i]] = i;
r[a[i]] = i;
}
int L = 1, R = r[a[1]];
int res = max(R - L - 1, 0);
while(L <= n) {
int maxv = 0;
for(int i = L + 1; i < R; i++) maxv = max(maxv, r[a[i]]);
if(maxv > R) {
res += max(maxv - R - 1, 0);
L = R;
R = maxv;
} else {
L = R + 1;
R = r[a[L]];
res += max(R - L - 1, 0);
}
}
printf("%d\n", res);
}
int main() {
int t = 1;
// scanf("%d", &t);
while(t--) solve();
return 0;
}
F. Flipping Range
题意
给定一个长度为n的数组a, 以及一个大小为m的集合b
每次选择一个x以及一段长度为x的连续子数组,将这段数组的所有值乘以-1
求进行若干次操作之后的最大值
思路
- 考虑可以进行操作的最小的长度为多少
假设, 则可以通过操作一次长度的x以及一次长度为y, 使得得到一次操作长度为x - y的效果, 由gcd(a, b) = gcd(b, a - b), 可得最小的操作长度为
问题等价于每次进行一次长度为g的操作, 使得的值最大
- 令s为长度为n的数组, 当为0时表示没有乘以-1, 当为1时表示乘以了-1, 对所有的按照进行分组, 将同一组的值进行异或得到
一共分成了g组, 每一个操作只会对组内的一个元素进行异或, 则所有要么同时等于1, 要么同时等于0
考虑dp:
code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <unordered_set>
using namespace std;
using ll = long long;
const int N = 1e6 + 10;
int n, m, b;
ll a[N];
ll dp[N][2];
void solve() {
scanf("%d%d", &n, &m);
for(int i = 0; i <= n; i++) dp[i][0] = 0, dp[i][1] = -2e9;
for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
int g = 0, b;
for(int i = 1; i <= m; i++) {
scanf("%d", &b);
g = __gcd(g, b);
}
for(int i = 1; i <= n; i++) {
int mod = i % g;
ll v0 = max(dp[mod][0] + a[i], dp[mod][1] - a[i]);
ll v1 = max(dp[mod][1] + a[i], dp[mod][0] - a[i]);
dp[mod][0] = v0;
dp[mod][1] = v1;
}
ll sum0 = 0, sum1 = 0;
for(int i = 0; i < g; i++) sum0 += dp[i][0], sum1 += dp[i][1];
printf("%lld\n", max(sum0, sum1));
}
int main() {
int t;
scanf("%d", &t);
while(t--) solve();
return 0;
}