Title: Codeforces Round #734 (Div. 3)
Date: 2021-07-25
Tag: CF
A. Polycarp and Coins
题目描述:
有一个人买东西付钱只用一元钱和二元钱, 现在他要付 n 元, 他使用一元钱的数量和二元钱数量的差值要最小, 问他付 n 元使用了多少一元钱和二元钱?
思路:
对
n % 3
进行分类套路即可,余0时一元等于二元,余1时就一元钱多一张,余2时就二元钱多一张
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<complex>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '
'
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
int t, n, m;
int tr[MAX];
int main(){
cin>>t;
while (t--) {
cin>>n;
int num1, num2;
m = n % 3;
if(m == 0){
num1 = num2 = n / 3;
cout<<num1<<' '<<num2<<endl;
}
else if(m == 1){
num1 = n / 3 + 1;
num2 = n / 3;
cout<<num1<<' '<<num2<<endl;
}
else {
num1 = n / 3;
num2 = n / 3 + 1;
cout<<num1<<' '<<num2<<endl;
}
}
return 0;
}
B1. Wonderful Coloring - 1
题目描述:
n
个字符,要么染成红色,要么染成绿色,要么不染色相同颜色的字符俩俩不同
红绿两种颜色的字符的数量相同
问:满足以上条件,染成红色的字符的数量是多少
思路:
因为相同的字符要染色的话只能染红色和绿色,所以只需要记录每个字符出现的次数,分数量为1的和数量
>=2
的去讨论记录即可
- 数量
>=2
的,红绿都可以染,就让ans += 2
- 数量等于
1
的,只能染红色或绿色,就让ans += 1
最后让
ans
除以2向下取整,即ans/=2
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<complex>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '
'
#define inf 0x3f3f3f3f
#define MAX 100000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
ll t, n, m;
string s;
map<char, int>mp;
int main(){
cin>>t;
while (t--) {
ll ans = 0;
mp.clear();
cin>>s;
for(int i = 0; i < s.size(); ++i){
++mp[s[i]];
}
n = mp.size();
map<char, int>::iterator it = mp.begin();
for(; it != mp.end(); ++it){
if(it->second < 2)ans += 1;
else ans += 2;
}
ans /= 2;
cout<<ans<<endl;
}
return 0;
}
B2. Wonderful Coloring - 2
题目描述:
恶心的构造题,艹
n
个数字,染成k
种颜色,也可以不染色- 相同颜色的数字染色不同
- 所有颜色的数量必须一样
- 染色的数量尽可能多
用0-k代表染色的颜色的种类(0代表不染色),让你构造出符合的染色方案
思路
首先和B1有点类似,统计每个数字出现的次数,再将所有次数
>=k
的搞出来,这些就是可以直接涂k
种颜色的,涂就行了; 对于次数<k
的集中到一起,为了防止相同数字染成一个颜色,所以就先按下标从小到达排个序,然后再算算能染几个k
种,就挨个染就行了,没操作的就赋0
这里操作起来还是比较恶心的,需要的准备的东西不少,得先用一个
vector
的数组存一下每个数出现的下标,便于后续操作,然后你还需要一个标记数组,对于>=k
的得标记一下,便于后面从1到n
循环找<k
的数字的时候可以分辨出来(其实也可以不用这样,在搞>=k
的if
下面写个else
搞一下就行了),还需要一个ans
数组去染色记录答案,还需要一个vector
数组存<k
的下标和数字,去排序啊干别的什么的还是比较容易超时的,建议清0的时候手动清零,别用
memset
,直接循环到n
去清,会快很多的
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<complex>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '
'
#define inf 0x3f3f3f3f
#define MAX 2000000 + 50
#define seed 13331
#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
int t, n, k, x;
vector<int>tr[MAX];
struct ran{
int id, val;
bool operator < (const ran &x)const{
return val < x.val;
}
};
vector<ran>v;
int main(){
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &k);
v.clear();
for(int i = 1; i <= n; ++i)tr[i].clear();
vector<int>vis(n + 1, 0);
vector<int>ans(n + 1, 0);
vector<int>ar(n + 1);
for(int i = 1; i <= n; ++i){
scanf("%d", &ar[i]);
tr[ar[i]].push_back(i);
}
for(int i = 1; i <= n; ++i){
if(tr[i].size() >= k){
for(int j = 0; j < k; ++j){
ans[tr[i][j]] = j + 1;
}
vis[i] = 1;
}
}
// for(int i = 1; i <= n; ++i)cout<<ans[i]<<' ';
// cout<<endl;
for(int i = 1; i <= n; ++i){
if(!vis[ar[i]]){
v.push_back({i, ar[i]});
}
}
sort(v.begin(), v.end());
ll num = v.size() / k * k;
int tot = 1;
for(int i = 0; i < num; ++i){
ans[v[i].id] = tot++;
if(tot == k + 1)tot = 1;
}
for(int i = 1; i <= n; ++i)cout<<ans[i]<<' ';
cout<<endl;
}
return 0;
}
C. Interesting Story
题目描述:
从
n
个只包含abcde
的字符串中选出若干个字符串,使得某一个字符的出现次数大于其他四个总和,问最多可以选多少个字符
思路:
写五个
sort
去暴力莽?!记录每个字符串中每个字符出现的次数,枚举abcde分别作为大于其他字符总和的情况
这里拿a做例子简单描述一下,假设每个字符串中a的数量为
tr[i].a
,其他的数量为tr[i].num
,则我们可以计算当前这个字符串对答案的贡献是tr[i].a - (tr[i].num - tr[i].a)
,即tr[i].a * 2 - tr[i].num
,将每个的贡献值塞到一个vector数组中去,然后从大到小排序,从头开始扫数组,用tot
记录当前的贡献总值,循环的时候看当前循环到的贡献加上tot
,如果> 0
说明当前的这个可以选,否则就直接退出循环对abcde都这样,然后取最大值即可
#include<map>
#include<set>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '
'
#define inf 0x3f3f3f3f
#define MAX 200000 + 50
#define seed 13331
//#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
int t, n;
string s;
struct ran{
int a, b, c, d, e;
int num;
}tr[MAX];
int get_a(){
vector<int>ans;
for(int i = 1; i <= n; ++i){
ans.push_back(tr[i].a * 2 - tr[i].num);
}
sort(ans.rbegin(), ans.rend());
int k = 0;
int tot = 0;
for(int i = 0; i < n; ++i){
if(k + ans[i] > 0){
++tot;
k += ans[i];
}
else break;
}
return tot;
}
int get_b(){
vector<int>ans;
for(int i = 1; i <= n; ++i){
ans.push_back(tr[i].b * 2 - tr[i].num);
}
sort(ans.rbegin(), ans.rend());
int k = 0;
int tot = 0;
for(int i = 0; i < n; ++i){
if(k + ans[i] > 0){
++tot;
k += ans[i];
}
else break;
}
return tot;
}
int get_c(){
vector<int>ans;
for(int i = 1; i <= n; ++i){
ans.push_back(tr[i].c * 2 - tr[i].num);
}
sort(ans.rbegin(), ans.rend());
int k = 0;
int tot = 0;
for(int i = 0; i < n; ++i){
if(k + ans[i] > 0){
++tot;
k += ans[i];
}
else break;
}
return tot;
}
int get_d(){
vector<int>ans;
for(int i = 1; i <= n; ++i){
ans.push_back(tr[i].d * 2 - tr[i].num);
}
sort(ans.rbegin(), ans.rend());
int k = 0;
int tot = 0;
for(int i = 0; i < n; ++i){
if(k + ans[i] > 0){
++tot;
k += ans[i];
}
else break;
}
return tot;
}
int get_e(){
vector<int>ans;
for(int i = 1; i <= n; ++i){
ans.push_back(tr[i].e * 2 - tr[i].num);
}
sort(ans.rbegin(), ans.rend());
int k = 0;
int tot = 0;
for(int i = 0; i < n; ++i){
if(k + ans[i] > 0){
++tot;
k += ans[i];
}
else break;
}
return tot;
}
int main(){
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for(int i = 1; i <= n; ++i)tr[i].a = tr[i].b = tr[i].c = tr[i].d = tr[i].e = tr[i].num = 0;
for(int i = 1; i <= n; ++i){
cin>>s;
tr[i].num = s.size();
for(int j = 0; j < s.size(); ++j){
if(s[j] == 'a')++tr[i].a;
else if(s[j] == 'b')++tr[i].b;
else if(s[j] == 'c')++tr[i].c;
else if(s[j] == 'd')++tr[i].d;
else if(s[j] == 'e')++tr[i].e;
}
}
int ans = 0;
ans = max(ans, get_a());
ans = max(ans, get_b());
ans = max(ans, get_c());
ans = max(ans, get_d());
ans = max(ans, get_e());
cout<<ans<<endl;
}
return 0;
}
D1. Domino (easy version)
题目描述:
在
n*m
为偶数的矩阵中放1 * 2
(横着)和2 * 1
(横着)的多米诺骨牌,现在给出k
个1 * 2
的牌,问你能不能再放k
个1 * 2
的情况下,用2 * 1
去放满矩阵
思路:
首先
n * m
是偶数有三种情况,也就是能放tot = n * m / 2
个牌
对于
n
和m
都是偶数的情况下,k
必须是偶数,才能满足条件对于
n
是奇数时,必须先把这行放完了,剩下的就是(n-1) * m
的矩阵,就变成了第一种情况,此时已经放了m/2
个横着的牌,所以让k -= m/2
, 能放的数量也少了m / 2个
,所有让tot -= m/2
对于
m
是奇数时,同理,必然先放n / 2
个竖着的,剩下的就是n * (m - 1)
的矩阵了,和第一种情况相同,此时只有tot -= n / 2
,横着的没有必须放的,就不操作k最后只要判断一下
k
的奇偶,以及tot
是否合法即可其实看了答案就发现真的就用了个比较简单的转换思想,将二三两种情况转换成
n和m
都偶数的情况,再去一步判断;但是自己就是想不到,就搁那瞎讨论,分这个奇偶分那个奇偶,太艹了
#include<map>
#include<set>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '
'
#define inf 0x3f3f3f3f
#define MAX 200000 + 50
#define seed 13331
//#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
int t;
int n, m, k, p, q;
int main(){
cin>>t;
while (t--) {
cin>>n>>m>>k;
q = n * m / 2;
if(n % 2 == 1){
k -= m / 2;
q -= m / 2;
}
if(m % 2 == 1){
q -= n / 2;
}
if(k < 0 || k % 2 == 1 || k > q)cout<<"NO
";
else cout<<"YES
";
}
return 0;
}
D2是个构造题,就是D1的一个升级版,不仅要判断能不能按D1的要求放,还要求你用字母a到z去填充这个牌,好像还要求相邻的不能重复,好像是挺麻烦的,不到500人A了,先鸪再说(连B2都没造出来,D2能造的出来??
E. Fixed Points
题目描述:
给定一个长度为
n
的数组a
和一个常数k
现在有一个操作,是删除
a
数组的任意一个,使得其右边的所有的数的下标-1
问你最少进行多少次操作能使得最终的数组
b
中至少有k
个数的b[i] = i
思路:
dp
状态:
dp[i] [j]
表示前i
个数中,删除j
个数能得到的b[h] = h(1<=h<=n)
的最大个数对于当前的ai有两种决策:
- 删,删了ai对
dp[i] [j]
没什么影响,所以dp[i] [j] = dp[i - 1] [j - 1]
- 不删, 就得看当前这个ai是否等于其下标了,由于他本身不删,所以删掉的
j
个就是在i - 1
之前删的,所以 ai的下标变成了i - j
,故dp[i] [j] = dp[i - 1] [j] + (a[i] == i - j)
#include<map>
#include<set>
#include<deque>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<string>
#include<vector>
#include<sstream>
#include<cstring>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define endl '
'
#define inf 0x3f3f3f3f
#define MAX 2000 + 50
#define seed 13331
//#define mod 1000000007
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define mem(a,b) memset((a),(b),sizeof(a))
typedef long long ll ;
typedef unsigned long long ull;
//不开longlong见祖宗!
inline int IntRead(){char ch = getchar();int s = 0, w = 1;while(ch < '0' || ch > '9'){if(ch == '-') w = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0';ch = getchar();}return s * w;}
int t;
int n, k;
int tr[MAX];
int dp[MAX][MAX];
int main(){
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; ++i)scanf("%d", &tr[i]);
for(int i = 1; i <= n; ++i){
for(int j = 0; j <= i; ++j){
if(j == 0)dp[i][j] = dp[i - 1][j] + (tr[i] == i);
else dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j] + (tr[i] == i - j));
}
}
int ans = -1;
for(int i = 0; i <= n; ++i){
if(dp[n][i] >= k){
ans = i;
break;
}
}
cout<<ans<<endl;
}
return 0;
}