[题目链接](https://codeforces.com/contest/1966)

A. Card Exchange

题意

给你n个牌,每个牌都有一个数字。

每次你可以选择恰好k个相同数字的牌, 然后将他们替换为任意的k-1张牌。(这k-1张牌的上的数字任意)

问在经过一些操作后,最终能剩下的牌的数量最少是多少。

思路

最终剩下牌的数量 = n - 最多执行的操作次数。

所以我们很明显地发现,如果起初不存在相同的k个数,那么就无法进行操作,直接输出n即可。

给出结论,如果可以进行第一次操作,那么就可以使得最终牌的数量变为k - 1。

下面是证明:

我们设当前有n张牌 , 在满足”存在相同的k张牌“的前提下, 有如下两种情况:

  1. 只有k张牌,且全部相同。那么再进行一次操作就可以实现最终剩下k-1张牌。
  2. 除了这k张牌之外,还有其他的牌(设他的值为num),那么我们就可以选择k-1张num来替换起初的k张相同牌, 这样就变为了总数为n-1的,仍然满足”存在相同的k张牌“的状态。最终会逐渐转化为第一种情况,并变为只剩k-1张牌的情况。

按照以上两种情况进行考虑,就可以保证最终会剩下k-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
#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> cnt(105);
rep(i,1,n){
int num;cin>>num;
cnt[num]++;
}
bool f = 0;
rep(i,1,100){
if(cnt[i] >= k ) f = 1;
}
if(!f){
cout<<n<<endl;
return ;
}else{
cout<<min(n,k-1)<<endl;
return ;
}
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

B. Rectangle Filling

题意

有一个n*m的表格,涂满了黑白两色。

规定如下操作: 每次选出两个相同颜色的格子, 让**”以他们为对角线的矩形区域“** 全部变为和选择方格同一种颜色。

在进行一些次数的操作后,能否使得整个n*m的表格全部变为某一种颜色。

思路

若四条边中同时存在某一种颜色,那么就可以将整个表格变为同一个颜色,否则就不可以。

比如有如下样例:

1
2
3
4
3 4
BWBB
WBWB
BBBW

我们可以将上面的表格变为白色, 只需从四个边中各自任意选出一个白色块W(可以重复选择某一块,如(3,4)这个W块,即作为靠下的边,也作为靠右边。)

我们选出至多四个块, 然后将他们作为操作时要选择的块,就可以使得整个表格变为这个颜色。

代码

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
#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,m;
cin>>n>>m;
int bl,br,bu,bd;
int wl,wr,wu,wd;
bl = bu = wl = wu = 0x3f3f3f3f;
br = bd = wr = wd = -0x3f3f3f3f;
rep(i,1,n){
rep(j,1,m){
char ch;cin>>ch;
if(ch == 'B'){
bl = min(bl,j);br = max(br,j);
bu = min(bu,i);bd = max(bd,i);
}else{
wl = min(wl,j);wr = max(wr,j);
wu = min(wu,i);wd = max(wd,i);
}
}
}
if(bl == 1 && br == m && bu == 1&&bd == n){
cout<<"YES\n";return ;
}
if(wl == 1 && wr == m && wu == 1&&wd == n){
cout<<"YES\n";return ;
}
cout<<"NO\n";
return ;
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

C. Everything Nim

题意

有n个堆, 第个堆初始有 个石头, 轮流进行操作,Alice先手,每次操作如下:

  1. 选择一个数x,保证且x不大于 任何非空堆 的剩余石头数量。

  2. 从所有的非空堆中都拿去x个石头。

最终无法操作(即所有堆全部为空)者会输掉比赛。

二人都选取最优策略,问谁会胜利

思路

我们先将堆按照从小到大的顺序排序, 并且去重。我们会从小到大地不断清空每一堆。

**情况一:**当某一堆和前一堆的数量只差1的时候, 清空这堆时别无他法,只能设置x为1,并拿走这堆的最后一个,(因为在清空前一个堆的时候已经将这一堆拿到只剩下1个石头)

**情况二:**如果这一堆和上一堆的数量差大于1,那么就可以选择

  1. 全部拿走,即让对手去清空下一堆。
  2. 拿到只剩一个, 即让自己去清空下一堆。

可以发现这两者会正好对应相反的结果, 而我们又可以从中任选一个,所以就一定会获胜。
于是就有如下结论:第一个遇到情况二的人会获胜。

注意考虑第一堆的数量情况,如果第一堆的数量大于2,那么Alice起手变获胜

代码

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
#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> a(n+5);
rep(i,1,n){
cin>>a[i];
}
sort(a.begin()+1,a.begin()+1+n);
bool f = 1;
int cnt = 0;
for(int i = 0;i<n;i++){
if(a[i] == a[i+1]) continue;
if(a[i] == a[i+1] - 1){
f ^= 1;
}else{
f^= 1;
break;
}
}
if(f)cout<<"Bob\n";
else cout<<"Alice\n";
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}

D. Missing Subsequence Sum

题意

给出n和k, 请你构造一个长度不大于25的数组, 保证:

  • a中不存在某个子序列的和为k
  • 对于任意 ,都有 **a中存在某个子序列的和为k**

思路

k将 分割为了两部分, ,我们分别构造这两部分。

对于部分, 我们使用二进制数组来构造 , 这些数的组合可以表示出 之间的任意数,于是我们设置 为”小于等于k的 最大的二次幂数“ , 即 。而 。 这样我们就可以构造出所有的 的数。

对于 部分,我们先选两个数 。 此时我们可以表示出之间的数,

我们设能表示的最大的数是mx, 那么下一个要表示的数就是 ,我们选择数 作为下一个要加的数,这时候就可以将”可表示区域“ 拓宽为 ,但是我们发现,如果 那么会导致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
#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;
int mx = 0;
int nx = 1;
vector<int> ans;
while(mx + (mx + 1) < k - 1){
ans.push_back(mx+1);
mx += mx + 1;
}
ans.push_back(k - 1 - mx);
mx = k - 1;
ans.push_back(k+1);
ans.push_back(k+2);
mx = k+2;
while(mx < n){
int tt = mx-k;
if(tt <= k){
ans.push_back(mx+1);
mx += mx+1;
continue;
}
ans.push_back(tt);
mx += tt;
}
cout<<ans.size()<<endl;
for(auto p : ans){
cout<<p<<" ";
}
puts("");
}
signed main(){
int T = 1;
cin>>T;
while(T--){
solve();
}
return 0;
}