[Codeforces Round 963 (Div. 2)](https://codeforces.com/contest/1993)

A. Question Marks

题意

有一场考试一共 道题目,其中答案为A,B,C,D 的题目各 道, 现在你有一份考试的结果,由字母A,B,C,D 组成,请问最多得到多少分。

思路

每种选项最多答对n道题目,我们统计每个选项的出现次数 ,和n取较小值 ,将结果加起来即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n;
cin>>n;
string str;
cin>>str;
int cnt[150] = {0};
for(auto & p : str){
cnt[p] ++;
}
int ans = 0;
for(int i = 'A';i<='D';i++){
ans += min(cnt[i],n);
}
cout<<ans<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

B. Parity and Sum

题意

给你一个由n个正整数组成数组a , 你每次操作可以选择两个下标 undefinedi,j)a_ia_ja_ia_ja_i+a_j$ 。

问最少需要操作几次才能够使得数组a中的所有的数奇偶性相同。

思路

我们将奇数和偶数分开来, 奇数在数组odd中,偶数在数组even中 , 那么我们每次操作即为从odd和even中各选择一个数, 最后目标为使odd数组为空或者even数组为空。

首先,如果odd数组初始为空,或者even初始数组为空,那么答案即为0 。

因为我们每次操作一定选择的是一个奇数和一个偶数,那么我们最终加和后的结果一定是奇数 ,于是每次操作只会使得奇数越来越多,偶数越来越少。

下面给出结论: 假设起初有个偶数 ,那么需要的操作次数最少为, 最多为

我们假设选择的奇数为 ,偶数为。 如果 那么会让奇数 变为另一个奇数 ; 如果 那么会让偶数变为奇数 。即只有第二种操作才能令偶数的数量减少一个。 因此最少需要cnt次才能将所有的偶数变为奇数。

因此我们应该尽可能地保证每次选择的奇数比偶数要大,于是我们每次一定会选择目前最大的奇数 , 如果在这种选择下仍然会出现奇数更小的情况,那么我们就一定无法在cnt次操作下完成目标了。 此时我们可以选择将mx和最大的偶数进行操作,得到的新的奇数一定比所有的偶数大,之后每次都选择这个奇数,便可以保证每次都满足奇数大于偶数了。 最终需要cnt+1次操作。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n;
cin>>n;
vector<int> odd,even;
for(int i =1;i<=n;i++){
int x;cin>>x;
if(x%2) odd.push_back(x);
else even.push_back(x);
}
if((!odd.size()&&even.size())) {cout<<"0"<<endl;return ;}
if((odd.size()&&!even.size())) {cout<<"0"<<endl;return ;}
sort(odd.begin(),odd.end());
sort(even.begin(),even.end());
int sz = even.size();
int mx = odd.back();
queue<int> q;
for(auto p:even) q.push(p);
int ans = 0;
while(q.size()){
if(mx > q.front()) {
ans++;
mx = mx + q.front();
q.pop();
}else{
ans ++;
mx = mx + q.front();
}
}
cout<<min(ans,sz+1)<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

C. Light Switches

题意

有n个房间,每个房间都有一盏灯,起初所有的灯都是灭的,第个房间会在 的时间刻转换灯的状态,即在 区间内亮灯。

问最早在哪个时刻,能保证所有n盏灯都是亮着的。如果不存在某个时刻所有的灯都是亮着的,那么输出-1 .

思路

题意即为将所有房间的亮灯区间取交集,然后取最小值。

我们可以对所有的房间按照 从小到大进行排序 ,然后依次取交集, 因为每个区间都为重复区间,所以每次取交集后一定也为 重复区间,于是我们只需要使用undefinedl,r)[l+j*2k,r+j*2k]$ 即可。

起初 ,之后我们遍历每个区间 , 如果当前遍历的区间与交集的初始区间 相差距离超过 便可以通过令 都加上(其中 ),来使得两个区间的左端点的距离不超过 , 此时如果 那么取交集一定为空,我们尝试让 再向后移动一个 ,试图令取交集。 (取交集即为

如果某次遍历时使得 那么此时交集已经为空,直接输出-1即可。

如果遍历结束,那么最终的答案即为

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
void solve(){
int n,k;
cin>>n>>k;
vector<int>a(n+5);
rep(i,1,n) cin>>a[i];
sort(a.begin()+1,a.begin()+1+n);
int l = a[1];
int r = a[1] + k - 1;
for(int i=2;i<=n;i++){
if(a[i] <= r && a[i] >= l){
l = a[i];
}

int t = (a[i] - l) / (2*k);
l += t * 2 * k;
r += t * 2 * k;
if(a[i] > r){
l+=2*k;
r+=2*k;
}
l = max(l,a[i]);
r = min(r,a[i]+k-1);
if(l > r) {
cout<<"-1\n";
return ;
}
}
cout<<l<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

D. Med-imize

题意

有一个长度为n的数组 (注意本题的数组下标从0开始) , 以及一个整数 , 我们每次选择一个长度为k的子数组,将其从数组a中删除,直到数组的中的数组元素数量不大于k。

问最终数组a的中位数最大是多少。本题的中位数为对数组的元素排序后,第undefinedfrac{sz+1}{2}sz$为数组的长度。

思路

我们可以二分答案来求最终的中位数 。于是问题变成了: 给定一个数mid ,能否存在一个最终数组 ,使得 中的大于等于mid的数的数量,比小于mid的数的数量更多。

我们将满足 的元素设置为 , 否则就设置为 。 我们尽可能让最终选出的 数组的和尽可能大,最后判断是否大于0即可。

但是我们不能任意选择剩下的元素,最终一定会剩下m个元素( ) ,并且最终剩下的数组 即选出的最终的下标为的数的原始下标一定在的同余集中。

很好证明这点: 假设我们选出了第3个数(即下标为2),那么之前一定进行过选择了2个数,然后又删除了几组数(每组一定为k个数),所以这个数的原始下标一定为

我们用 表示考虑前个数,数组最大为多少,由于我们每次只能成组地删除元素,那么就代表了如果我们选择不要 这个元素,那么 , 而如果我们选择要这个元素,那么 , 最终比较 是否成立即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
using namespace std;
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define rep(i,l,r) for(int i = l;i<=r;i++)
#define per(i,r,l) for(int i = r;i>=l;i--)
const int INF = 0x3f3f3f3f3f3f3f3f;
typedef pair<int,int> PII;
// 本题数据在数组中从下标0开始存储
int a[500005];
int b[500005];
int dp[500005];
int n,k;
bool check(int mid){
for(int i = 0;i < n;i++){
if(a[i] >= mid) b[i] = 1;
else b[i] = -1;
}
dp[0] = b[0];
for(int i = 1;i<n;i++){
if(i%k == 0){
dp[i] = max(dp[i-k],b[i]);
}else{
dp[i] = dp[i-1] + b[i];
if(i > k)
dp[i] = max(dp[i],dp[i-k]);
}
}
return dp[n-1] > 0;
}
void solve(){
cin>>n>>k;
rep(i,0,n-1) cin>>a[i];
int l,r;
l = 1;r = 1e9;
while(l<r){
int mid = (l+r+1)>>1;
if(check(mid)){
l = mid;
}else{
r = mid-1;
}
}
cout<<l<<'\n';
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}