A
随意交换a[i]a[i]a[i]和b[i]b[i]b[i],问max{a[i]}×max{b[i]}max\{a[i]\}\times max\{b[i]\}max{a[i]}×max{b[i]}的最小值
- 因为两个数越靠近乘积越大,所以我们尽可能地让大数在aaa数组中,让小数在bbb数组中,然后分别取最大值乘积即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
vector<int> a(n), b(n);
for(auto &i : a) cin >> i;
for(auto &i : b) cin >> i;
int mxa = -1;
int mxb = -1;
for(int i=0;i<n;i++){
if(a[i] < b[i]) swap(a[i], b[i]);
mxa = max(mxa, a[i]);
mxb = max(mxb, b[i]);
}
cout << mxa * mxb << '\n';
}
return 0;
}
B
给你一个数组,现在可以进行一种操作,用后面的连续kkk项覆盖之前的连续kkk项,问将这个数组变成同一个数所需要的最少操作数
- 显然应该从后往前覆盖,而且最后的数组的值一定等于最后一个数,所以可以记录当前长度从后往前遍历
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
vector<int> a(n + 1);
for(int i=n;i>=1;i--) cin >> a[i];
int ans = 0;
int r = 1;
for(int i=1;i<=n;i++){
if(a[i] != a[1]){
while(i > r){
r *= 2;
ans += 1;
}
}else{
r = max(r, i);
}
}
cout << ans << '\n';
}
return 0;
}
C
让你把[0,n−1][0,n-1][0,n−1]这nnn个数分成两组,满足∑a[i]&b[i]=k\sum a[i]\&b[i]=k∑a[i]&b[i]=k,保证nnn是222的幂次,k∈[0,n−1]k\in[0,n-1]k∈[0,n−1]
- 我们首先知道,因为nnn是222的幂次,所以在本题中,只要满足a+b=n−1a+b=n-1a+b=n−1,或者使用取反后再和n作与运算的方式,那么就有a&b=0a\&b=0a&b=0
- 我们可以考虑一种构造方法,如果设c(k)c(k)c(k)和kkk构成二进制互补,我们只需要选择0,n−1,c(k),k0,n-1,c(k),k0,n−1,c(k),k这几个数即可实现题目要求,因为0&c(k)=0,(n−1)&k=k0\&c(k)=0,(n-1)\&k=k0&c(k)=0,(n−1)&k=k,这样就满足了题目要求,且我们让其余的数都去找它们各自互补的数即可
- 但是我们需要特判k=0k=0k=0和k=n−1k=n-1k=n−1,因为上述方案使用了这两个特殊值,k=0k=0k=0时只需要让所有数都和与它互补的数字配对即可;k=n−1k=n-1k=n−1的时候,我们可以选择这样几组特殊值(n−1,n−2),(n−3,1),(0,2)(n-1,n-2),(n-3,1),(0,2)(n−1,n−2),(n−3,1),(0,2),这里发现n≥4n\geq4n≥4,且n=4n=4n=4的时候发现111使用了两次,所以进行一次特判,发现n=4,k=3n=4,k=3n=4,k=3时无解,按照上述构造方法只有这一种情况无解,然后其余选择与它本身配对的
- 此题很巧妙
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n, k;
cin >> n >> k;
if(n == 4 && k == 3){
cout << -1 << '\n';
continue;
}
if(k == 0){
vector<bool> vis(n + 1);
for(int i=0;i<n;i++){
int now = n - 1 - i;
if(vis[i] || vis[now]) continue;
cout << i << ' ' << now << '\n';
vis[i] = vis[now] = true;
}
}else if(k == n - 1){
cout << n - 1 << ' ' << n - 2 << '\n';
cout << n - 3 << ' ' << 1 << '\n';
cout << 0 << ' ' << 2 << '\n';
vector<bool> vis(n + 1);
vis[n - 1] = vis[n - 2] = vis[n - 3] = vis[1] = vis[0] = vis[2] = true;
for(int i=0;i<n;i++){
int now = n - 1 - i;
if(vis[now] || vis[i]) continue;
cout << now << ' ' << i << '\n';
vis[now] = vis[i] = true;
}
}else{
cout << 0 << ' ' << n - 1 - k << '\n';
cout << n - 1 << ' ' << k << '\n';
vector<bool> vis(n + 1);
vis[0] = vis[n - 1] = vis[n - 1 - k] = vis[k] = true;;
for(int i=0;i<n;i++){
int now = n - 1 - i;
if(vis[now] || vis[i]) continue;
cout << i << ' ' << now << '\n';
vis[i] = vis[now] = true;
}
}
}
return 0;
}
D
让你把一个数组分成kkk个子数组,让你规定一个范围[x,y][x,y][x,y],使得每个子数组中的元素值在这个范围里的元素数量严格大于在这个范围之外的元素数量,现在要让y−xy-xy−x尽可能的小,现在求这个范围还有划分若干子数组的方案
- 假设我们已经知道了x,yx,yx,y,那么我们怎么划分数组呢?枚举左端点,如果发现当前满足子数组要求,那么就让当前为一个子数组,换句话说,如果这个数组就一个数满足条件,那么就让这个数组里面就一个数,越少越好,这样的构造是合法的;如果当前这个数不满足条件,那么就继续往后走,直到数组中元素满足条件时再输出
- 现在关键问题在于如何确定x,yx,yx,y,首先我们来看一下至少要有多少个数在[x,y][x,y][x,y]里面,设为valvalval,有val≥n−val+kval\geq n-val+kval≥n−val+k,那么val≥n+k2val\geq \frac{n+k}{2}val≥2n+k,根据元素数量进行枚举左右端点,从而最小化区间长度
- 参考T神代码,写的实在漂亮
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n, k;
cin >> n >> k;
vector<int> a(n);
for(int i=0;i<n;i++) cin >> a[i];
auto b = a;
sort(b.begin(), b.end());
int in = (n + k + 1) / 2;
int x = -1;
int y = n + 1;
for(int i=0;i<=n-in;i++){
int L = b[i];
int R = b[i + in - 1];
if(R - L < y - x){
x = L;
y = R;
}
}
cout << x << ' ' << y << '\n';
int mx = -1;
int cnt = 0;
int last = -1;
for(int i=0;i<n;i++){
cnt += ((x <= a[i] && a[i] <= y) ? 1 : -1);
if(cnt > mx){
mx = cnt;
if(cnt >= 1 && cnt < k){
cout << last + 2 << ' ' << i + 1 << '\n';
last = i;
}
}
}
cout << last + 2 << ' ' << n << '\n';
}
return 0;
}