A Min Max Swap
题意
给定长度为n的数组a和b,可以进行如下任意次数的操作:
选择一个下标
i
(
1
≤
i
≤
n
)
i (1 \le i \le n )
i(1≤i≤n), 交换
a
[
i
]
a[i]
a[i] 和
b
[
i
]
b[i]
b[i]
求经过任意次数的操作后,
m
a
x
(
a
1
,
a
2
,
.
.
.
,
a
n
)
∗
m
a
x
(
b
1
,
b
2
,
.
.
.
,
b
n
)
max(a_1, a_2, ..., a_n) * max(b_1, b_2, ..., b_n)
max(a1,a2,...,an)∗max(b1,b2,...,bn) 的最小值
思路
可以发现a,b中最大的元素对于乘积必然会有贡献,要使得乘积最小,就要使得最大的元素不在另一个数组的最大值尽可能的小
故我们可以选择所有的
a
[
i
]
>
b
[
i
]
a[i] \gt b[i]
a[i]>b[i]的
i
i
i,交换二者的值,答案即为交换后的两个数组的最大值相乘
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的子数组
(
1
≤
l
≤
l
+
2
∗
k
−
1
≤
n
,
k
≥
1
)
(1 \le l \le l + 2 * k - 1 \le n, k \ge 1)
(1≤l≤l+2∗k−1≤n,k≥1), 对于每一个
i
(
0
≤
i
≤
k
−
1
)
i(0 \le i \le k - 1)
i(0≤i≤k−1),令
a
l
+
i
=
a
l
+
k
+
i
a_{l + i} = a_{l + k + i}
al+i=al+k+i
求使得数组的所有元素相等的最小操作次数
思路
操作实际上就是选择一个长度为2k的子数组,将后一半的数组赋值到前一半的数组中
可以发现数组中的最后一个元素是无法修改成其他元素的,那么数组最后必然全部为最后一个元素的值
我们可以从最后第二个个元素开始,如果当前元素
a
[
i
]
a[i]
a[i]和后面的元素(也就是最后一个元素)不相等的话,令
k
=
n
−
i
k = n - i
k=n−i,进行一次操作,操作之后
i
=
i
−
k
i = i - k
i=i−k, 直到
i
<
1
i < 1
i<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];
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
求长度为
n
/
2
n / 2
n/2的数组a和b, a和b中的元素为0,1,2,…n - 1, 每一个数字只能出现一次
并且
∑
i
=
1
n
/
2
a
i
a
n
d
b
i
=
k
\sum_{i=1}^{n/2} a_{i} \, and \, b_{i} = k
∑i=1n/2aiandbi=k
思路
假如 n = 8, k = 0, 构造如下:
a
1
=
00
0
2
b
1
=
11
1
2
a_{1} = 000_{2} \quad b_{1} = 111_{2}
a1=0002b1=1112
a
2
=
00
1
2
b
2
=
11
0
2
a_{2} = 001_{2} \quad b_{2} = 110_{2}
a2=0012b2=1102
a
3
=
01
0
2
b
3
=
10
1
2
a_{3} = 010_{2} \quad b_{3} = 101_{2}
a3=0102b3=1012
a
4
=
01
1
2
b
4
=
10
0
2
a_{4} = 011_{2} \quad b_{4} = 100_{2}
a4=0112b4=1002
当
1
<
k
<
n
1 \lt k \lt n
1<k<n时, 只需要将
00
0
2
000_2
0002与对应k的所在的位置交换即可
当 k = = n − 1 k == n - 1 k==n−1 时(n = 4时无解), 只需要将 a 1 , b 2 , a 4 a_1, b_2, a_4 a1,b2,a4的值调换一下即可
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个连续子数组(
1
≤
k
≤
n
1 \le k \le n
1≤k≤n), 并且每一个子数组中的元素在[x, y]范围内的数量要严格大于不在[x, y]范围内的数量(在范围内是指
x
≤
a
i
≤
y
x \le a_i \le y
x≤ai≤y)
求最小的y - x, 输出x, y以及分割后的k的数组
思路
- 假设已经给定[x, y],如何判断[x, y]是否满足要求并且找到对应分割的数组
构造数组b, b i = [ x ≤ a i ≤ y ] ? 1 : − 1 b_i = [x \le a_i \le y] ? 1 : -1 bi=[x≤ai≤y]?1:−1
数组sum为数组b的前缀和数组
每一个子数组中的元素在[x, y]范围内的数量要严格大于不在[x, y]范围内的数量等价于数组a中在[x, y]范围内的数量减去不[在x, y]范围内的数量 ≥ k \ge k ≥k, 同时也等价于 s u m n ≥ k sum_n \ge k sumn≥k
构造分割的数组:
令f[x]为x第一次在前缀数组sum出现的下标 ( 1 ≤ x ≤ k ) (1 \le x \le k) (1≤x≤k)
按照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, 开始时所有的
c
i
=
0
c_i = 0
ci=0, 并且
1
≤
a
i
≤
n
1 \le a_i \le n
1≤ai≤n
可以对数组进行如下的操作:
选择三个下标
i
,
j
,
k
(
1
≤
i
<
j
<
k
≤
n
)
,
并
且
C
i
=
c
j
=
c
k
=
0
,
a
i
=
a
k
i, j, k(1 \le i \lt j \lt k \le n), 并且C_i = c_j = c_k = 0, a_i = a_k
i,j,k(1≤i<j<k≤n),并且Ci=cj=ck=0,ai=ak, 然后设置
c
j
=
1
c_j = 1
cj=1
求进行一系列操作后
∑
i
=
1
n
c
i
\sum_{i = 1}^{n}c_i
∑i=1nci可能的最大值
思路
只有出现两次以上的数字才可能满足上述操作要求的i和k, 并且出现大于两次的数字来说,选择第一个和最后一个是最优的
使用l[i]和r[i]记录a[i]出现的第一个和最后一个位置
对于每一段[L, R], 能够做出的贡献为max(R - L - 1, 0)
并且如果存在相交的线段, 即
i
∈
[
L
+
1
,
R
−
1
]
的
r
[
a
[
i
]
]
>
R
i \in [L + 1, R - 1]的r[a[i]] \gt R
i∈[L+1,R−1]的r[a[i]]>R则[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
(
1
≤
m
≤
⌊
n
/
2
⌋
,
1
≤
b
i
≤
⌊
n
/
2
⌋
)
(1 \le m \le \lfloor n / 2 \rfloor, 1 \le b_i \le \lfloor n / 2 \rfloor)
(1≤m≤⌊n/2⌋,1≤bi≤⌊n/2⌋)
每次选择一个x
(
x
∈
b
)
(x \in b)
(x∈b)以及一段长度为x的连续子数组,将这段数组的所有值乘以-1
求进行若干次操作之后
∑
i
=
1
n
a
i
\sum_{i=1}^{n}a_i
∑i=1nai的最大值
思路
- 考虑可以进行操作的最小的长度为多少
假设 x ∈ b a n d y ∈ b x \in b \, and \, y \in b x∈bandy∈b, 则可以通过操作一次长度的x以及一次长度为y, 使得得到一次操作长度为x - y的效果, 由gcd(a, b) = gcd(b, a - b), 可得最小的操作长度为 g = g c d ( b 1 , b 2 , . . . b m ) g = gcd(b_1, b_2,...b_m) g=gcd(b1,b2,...bm)
问题等价于每次进行一次长度为g的操作, 使得 ∑ i = 1 n a i \sum_{i=1}^{n}a_i ∑i=1nai的值最大
- 令s为长度为n的数组, 当 s i s_i si为0时表示 a i a_i ai没有乘以-1, 当 s i s_i si为1时表示 a i a_i ai乘以了-1, 对所有的 s i s_i si按照 i m o d g i \quad mod \quad g imodg进行分组, 将同一组的值进行异或得到 f x ( x 为 i m o d g ) f_x(x为i \quad mod \quad g) fx(x为imodg)
一共分成了g组, 每一个操作只会对组内的一个元素进行异或, 则所有 f x f_x fx要么同时等于1, 要么同时等于0
考虑dp:
d p [ i ] [ 0 ] 为 ∑ k = 0 i − k ∗ g ≥ 0 a i − k ∗ g 并 且 f i m o d g = 0 的 最 大 值 dp[i][0]为\sum_{k = 0}^{i - k * g \ge 0}a_{i - k * g} 并且f_{i \quad mod \quad g} = 0的最大值 dp[i][0]为∑k=0i−k∗g≥0ai−k∗g并且fimodg=0的最大值
d p [ i ] [ 1 ] 为 ∑ k = 0 i − k ∗ g ≥ 0 a i − k ∗ g 并 且 f i m o d g = 1 的 最 大 值 dp[i][1]为\sum_{k = 0}^{i - k * g \ge 0}a_{i - k * g} 并且f_{i \quad mod \quad g} = 1的最大值 dp[i][1]为∑k=0i−k∗g≥0ai−k∗g并且fimodg=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 = 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;
}