Codeforces Round #748 (Div. 3) 题解
文章目录
A. Elections
-
题意
给定数值 a , b , c a,b,c a,b,c,问其中一个数大于其他两个数还需要增加多大。 -
解题思路
签到。 -
AC代码
/**
*@filename:A_Elections
*@author: pursuit
*@created: 2021-10-13 22:36
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, a, b, c;
void solve(){
int res1 = max(b, c), res2 = max(a, c), res3 = max(a, b);
if(a > res1){
res1 = 0;
}
else{
res1 = res1 - a + 1;
}
if(b > res2){
res2 = 0;
}
else{
res2 = res2 - b + 1;
}
if(c > res3){
res3 = 0;
}
else{
res3 = res3 - c + 1;
}
cout << res1 << " " << res2 << " " << res3 << endl;
}
int main(){
cin >> t;
while(t -- ){
cin >> a >> b >> c;
solve();
}
return 0;
}
B. Make it Divisible by 25
-
题意
给定一个数值字符串,每次操作你都可以从中删除一个字符,问至少删除多少字符才可以使得剩余字符串能否整除 25 25 25。 -
解题思路
能整除 25 25 25的尾部两个数为 00 , 25 , 50 , 75 00,25,50,75 00,25,50,75。所以我们判断需要形成这样的尾部数最少需要删除多少字符即可。 -
AC代码
/**
*@filename:BBB
*@author: pursuit
*@created: 2021-10-13 22:40
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t;
string s;
void solve(){
int res = INF;
string temp = "00";
int cnt = 0, idx = 0;
bool flag = false;
for(int i = s.size() - 1; i >= 0; -- i){
if(s[i] == temp[idx]){
++ idx;
if(i == 0)flag = true;
if(idx == temp.size())break;
}
else{
++ cnt;
}
}
if(!flag && idx == temp.size()){
res = min(res, cnt);
}
temp = "52";
cnt = idx = 0;
for(int i = s.size() - 1; i >= 0; -- i){
if(s[i] == temp[idx]){
++ idx;
if(idx == temp.size())break;
}
else{
++ cnt;
}
}
if(idx == temp.size()){
res = min(res, cnt);
}
temp = "05";
cnt = idx = 0;
for(int i = s.size() - 1; i >= 0; -- i){
if(s[i] == temp[idx]){
++ idx;
if(idx == temp.size())break;
}
else{
++ cnt;
}
}
if(idx == temp.size()){
res = min(res, cnt);
}
temp = "57";
cnt = idx = 0;
for(int i = s.size() - 1; i >= 0; -- i){
if(s[i] == temp[idx]){
++ idx;
if(idx == temp.size())break;
}
else{
++ cnt;
}
}
if(idx == temp.size()){
res = min(res, cnt);
}
cout << res << endl;
}
int main(){
cin >> t;
while(t -- ){
cin >> s;
solve();
}
return 0;
}
C. Save More Mice
-
题意
有一只猫位于 0 0 0处,第 i i i只老鼠位于 x i x_i xi处,每秒猫向右移动一格,同时你可以控制一只老鼠移动一格,当猫到达老鼠的位置则会吃掉老鼠,老鼠到达 n n n处即可逃走,问 n n n秒后你最多可以拯救几只老鼠。 -
解题思路
贪心的想肯定是将能救的都救了,所以对位置进行排序,先救离猫远的即可。 -
AC代码
/**
*@filename:C_Save_More_Mice
*@author: pursuit
*@created: 2021-10-13 23:00
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 4e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, m;
int x[N];
ll res = 0;
void solve(){
sort(x + 1, x + 1 + m);
int cnt = n, res = 0;
for(int i = m; i >= 1; -- i){
if(cnt > n - x[i]){
++ res;
cnt -= (n - x[i]);
}
else{
break;
}
}
printf("%d\n", res);
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i){
scanf("%d", &x[i]);
}
solve();
}
return 0;
}
D1. All are Same
-
题意
给定 n n n个整数,请你选定一个 k k k使得所有整数通过减 k k k操作变成相同的值,若 k k k可以取无穷大,则输出 − 1 -1 −1。 -
解题思路
既然需要变成相同的值,我们肯定都是想变成所有整数中的最小值,那么我们需要去弥补这 n n n个整数与最小值的差值 d d d,如果可以弥补,说明我们选取的 k k k一定是 d d d的因子。至此,我们只需要求出所有差值的最大公约数则是我们选取的 k k k的最大值了。 -
AC代码
/**
*@filename:D1_All_are_Same
*@author: pursuit
*@created: 2021-10-13 23:11
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, a[N];
int minn;
bool check(){
for(int i = 2; i<= n; ++ i){
if(a[i] != a[1])return false;
}
return true;
}
void solve(){
if(check()){
puts("-1");
}
else{
int gcd = 0;
for(int i = 1; i <= n; ++ i){
gcd = __gcd(gcd, a[i] - minn);
}
cout << gcd << endl;
}
}
int main(){
cin >> t;
while(t -- ){
cin >> n;
minn = INF;
for(int i = 1; i <= n; ++ i){
cin >> a[i];
minn = min(minn, a[i]);
}
solve();
}
return 0;
}
D2. Half of Same
-
题意
和D1唯一的区别就是我们需要保证 n n n个整数中有一半的整数可以相同。 -
解题思路
会了D1,实际上D2也是一样,只是我们无法确定该选择哪些数。如果相同值的数达到了一半,那么 k k k可以取无穷大。如果没有,那么我们可以先将所有的差值处理出来,则我们的 k k k一定是从这些差值的因子中诞生的。很容易证明因子数量不会超过 1 e 4 1e4 1e4。所以枚举这些因子 d d d,同时枚举一个我们需要变成的数,即前一半小的任何一个数。然后判断之后的数与其差值是否为 d d d的倍数。至此题解。注意处理过程中使用 s e t set set提高效率。 -
AC代码
/**
*@filename:D2_Half_of_Same
*@author: pursuit
*@created: 2021-10-16 16:36
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, a[N];
void solve(){
sort(a + 1, a + 1 + n);
int maxx = 0, cnt = 1;
for(int i = 2; i <= n; ++ i){
if(a[i] != a[i - 1]){
maxx = max(cnt, maxx);
cnt = 1;
}
else ++ cnt;
}
maxx = max(maxx, cnt);
if(maxx * 2 >= n)puts("-1");
else{
//存所有因子。
set<int> v1, v2;
for(int i = 1; i <= n; ++ i){
for(int j = i + 1; j <= n; ++ j){
v1.insert(a[j] - a[i]);
}
}
for(auto x : v1){
for(int j = 1; j * j <= x; ++ j){
if(x % j)continue;
v2.insert(j), v2.insert(x / j);
}
}
int res = 0;
for(auto x : v2){
//枚举要变成的数。
for(int i = 1; i <= n / 2 + 1; ++ i){
int cnt = 1;
for(int j = i + 1; j <= n; ++ j){
if((a[j] - a[i]) % x == 0)++ cnt;
}
if(cnt * 2 >= n){
res = x;
}
}
}
cout << res << endl;
}
}
int main(){
cin >> t;
while(t -- ){
cin >> n;
for(int i = 1; i <= n; ++ i){
cin >> a[i];
}
solve();
}
return 0;
}
E. Gardener and Tree
-
题意
给定一颗树,每次操作都可以将叶子结点全部删掉,问 k k k次操作之后,树还剩下几个结点。 -
解题思路
度为 1 1 1的点则是叶子结点,我们首先需要将所有结点的度处理出来,然后从这些点出发跑bfs,模拟删点过程(即其相邻点的度减 1 1 1),然后同样将度为 1 1 1的叶子结点放入,注意我们需要存储当前是进行第几次操作,这样可以在完成 k k k次操作之后退出。
注意特判 n = 1 n=1 n=1的时候,此时没有边,由于 k ≥ 1 k\geq1 k≥1,所以答案为 0 0 0。 -
AC代码
/**
*@filename:E_Gardener_and_Tree
*@author: pursuit
*@created: 2021-10-13 23:21
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 4e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, n, k, tot;
bool vis[N];
struct Edge{
int to, next;
}edges[N << 1];
struct node{
int u, cnt;
};
int head[N], du[N];
void add(int u, int v){
edges[++ tot].to = v;
edges[tot].next = head[u];
head[u] = tot;
}
void init(){
for(int i = 1; i <= n; ++ i)head[i] = du[i] = 0;
tot = 0;
}
void solve(){
if(n == 1){
puts("0");
return;
}
queue<node> q;
int res = n;
for(int i = 1; i <= n; ++ i){
if(du[i] == 1){
-- du[i];
q.push({i, 1});
-- res;
}
}
while(!q.empty()){
node temp = q.front();
if(temp.cnt == k || res == 0)break;
q.pop();
for(int i = head[temp.u]; i; i = edges[i].next){
int v = edges[i].to;
-- du[v];
if(du[v] == 1)q.push({v, temp.cnt + 1}), -- res;
}
}
printf("%d\n", res);
init();
}
int main(){
scanf("%d", &t);
while(t -- ){
scanf("%d%d", &n, &k);
tot = 0;
for(int i = 1, u, v; i < n; ++ i){
scanf("%d%d", &u, &v);
add(u, v), add(v, u);
++ du[u], ++ du[v];
}
solve();
}
return 0;
}
F. Red-Black Number
-
题意
给你一个字符串 s s s,你需要对其中字符进行染色,红色的字符连在一起组成一个字符串数值为 A A A,黑色的字符连在一起组成一个字符串数值为 B B B,需要满足 A A A能整除 a a a, B B B能整除 b b b。问其中 A A A和 B B B位数相差最小值为多少。 -
解题思路
即对于字符串中的字符我们是染成红色还是染成黑色,这样乍一看我们有 2 n 2^n 2n种选择。可我们又注意到 a a a和 b ≤ 40 b\leq 40 b≤40,所以取余有 40 40 40种可能,而 n ≤ 40 n\leq 40 n≤40,我们可以用 d p [ u ] [ m a ] [ m b ] [ c n t a ] dp[u][ma][mb][cnta] dp[u][ma][mb][cnta]表示考虑第 u u u位且当前选取数 A A A模 a a a为 m a ma ma,当前选取数 B B B模 b b b为 m b mb mb,且 A A A为 c n t a cnta cnta位的状态是否被访问过。那么状态数量为 4 0 4 = 2.56 × 1 0 6 40^4=2.56\times 10^6 404=2.56×106,故我们可以 d f s dfs dfs模拟选取,加上记忆化搜索即可通过。 -
AC代码
/**
*@filename:F_Red_Black_Number
*@author: pursuit
*@created: 2021-10-16 15:58
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 50 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
bool dp[N][N][N][N];//dp[u][ma][mb][cnta]表示考虑第u位且当前选取数A模a为ma,当前选取数B模b为mb,且A为cnta位的状态是否被访问过.
int t, n, a, b, res;
bool vis[N], ok[N];//vis[i]表示第i个数是否被A选取。
string s;
void dfs(int u, int ma, int mb, int cnta){
if(u == n){
if(!ma && !mb && cnta && cnta < n){
if(abs(n - 2 * cnta) < res){
res = abs(n - 2 * cnta);
for(int i = 0; i < n; ++ i)ok[i] = vis[i];
}
}
return;
}
if(dp[u][ma][mb][cnta])return;
dp[u][ma][mb][cnta] = true;
vis[u] = false;
dfs(u + 1, ma, (mb * 10 + s[u] - '0') % b, cnta);
vis[u] = true;
dfs(u + 1, (ma * 10 + s[u] - '0') % a, mb, cnta + 1);
}
void solve(){
dfs(0, 0, 0, 0);
if(res == 1e9){
puts("-1");
return;
}
for(int i = 0; i < n; ++ i){
if(ok[i])printf("R");
else printf("B");
}
puts("");
}
int main(){
cin >> t;
while(t -- ){
cin >> n >> a >> b;
cin >> s;
res = 1e9;
memset(dp, false, sizeof(dp));
solve();
}
return 0;
}
G. Changing Brackets
-
题意
给你一个包含[,],(,)的字符串序列,你可以进行两种操作,第一种操作是将(和)转换,[和]相互转换,这种操作不需要花费,另一种操作是将[变成(,]变成)。先有q次查询,每次查询给定[l,r],问 s l . . s r s_l..s_r sl..sr这一段字符串序列变成合法括号序列的最小花费。 -
解题思路
我们发现,如果对于只有()括号的序列,那么是不用任何花费的。关键在于处理[],我们肯定是尽可能不进行操作 2 2 2。我们发现,奇数位置上的中括号和偶数位置上的中括号进行组合且合法,因为中间都是小括号,而这两个中括号可以通过操作 1 1 1匹配。所以我们可以用前缀和处理表示 s u m [ i ] sum[i] sum[i]前 i i i个字符串序列还有多少中括号没有配对,那么答案自然为 a b s ( s u m [ r ] − s u m [ l − 1 ] ) abs(sum[r]-sum[l-1]) abs(sum[r]−sum[l−1]),加上绝对值是因为万一偶数位置比奇数位置多这种情况。理解抵消这个操作这道题即可解。 -
AC代码
/**
*@filename:G_Changing_Brackets
*@author: pursuit
*@created: 2021-10-16 17:21
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e6 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int t, q, sum[N], l, r;
string s;
void solve(){
cin >> l >> r;
cout << abs(sum[r] - sum[l - 1]) << endl;
}
int main(){
cin >> t;
while(t -- ){
cin >> s >> q;
for(int i = 0; i < s.size(); ++ i){
sum[i + 1] = sum[i];
if(s[i] == '[' || s[i] == ']'){
if(i % 2)++ sum[i + 1];
else -- sum[i + 1];
}
}
while(q -- ){
solve();
}
}
return 0;
}

浙公网安备 33010602011771号