Codeforces Round 920 (Div. 3) A~G

2024-01-18 06:04
文章标签 codeforces round div 920

本文主要是介绍Codeforces Round 920 (Div. 3) A~G,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

A.Square(思维)

题意:

给出正方形四个点的坐标,求出该正方形的面积。

分析:

统计行列坐标的最大最小值,然后计算得到边长,通过边长即可得到面积。

代码:

#include<bits/stdc++.h>using namespace std;void solve() {int maxx = -1001, minx = 1001;for (int i = 0; i < 4; i++) {int x, y;cin >> x >> y;maxx = max(maxx, x);minx = min(minx, x);}cout << (maxx - minx) * (maxx - minx) << endl;
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

B.Arranging Cats(贪心)

题意:

n n n个盒子,每个盒子分为两种状态:

  • b i = 1 b_i = 1 bi=1:第 i i i个盒子中有一只猫

  • b i = 0 b_i = 0 bi=0:第 i i i个盒子中没有猫

你可以进行若干次操作,每次操作可以选择以下三种操作中的一种:

  1. 把一只猫放入空的盒子中

  2. 把一只猫从盒子里拿出来

  3. 从某个盒子里拿出一只猫,并把这只猫放到另一个盒子里

给出目前盒子的状态序列: s 1 , . . . , s n s_1, ..., s_n s1,...,sn,问将该序列修改为目标序列: f 1 , . . . , f n f_1, ..., f_n f1,...,fn至少需要几次操作。

分析:

首先思考应该优先选择哪个操作,不难发现操作3能同时改变两个盒子的状态,另外两种操作只能改变一个盒子的状态,因此,要优先选择操作3.

定义 a d d add add为序列中 s i = 0 s_i = 0 si=0 f i = 1 f_i = 1 fi=1的个数, s u b sub sub为序列中 s i = 1 s_i = 1 si=1 f i = 0 f_i = 0 fi=0的个数。

那么可以使用的操作3的次数即为 m i n ( a d d , s u b ) min(add, sub) min(add,sub),同时不难发现,剩余所需的操作为 ∣ a d d − s u b ∣ |add - sub| addsub,因此实际所需的总操作次数为 m a x ( a d d , s u b ) max(add, sub) max(add,sub),统计完直接输出即可。

代码:

#include<bits/stdc++.h>using namespace std;
const int N = 1e5 + 5e2;int n;
string a, b;void solve() {cin >> n >> a >> b;int add = 0, sub = 0;for (int i = 0; i < n; i++) {if (a[i] > b[i]) sub++;else if (a[i] < b[i]) add++;}cout << max(sub, add) << endl;
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

C.Sending Messages(贪心)

题意:

n n n封邮件需要发送,这些邮件需要在 m 1 , m 2 , . . . , m n m_1, m_2, ..., m_n m1,m2,...,mn时间发送,而邮件均需使用手机发送,手机一开始(0时刻)拥有 f f f格电,且如果处于开机状态那么每单位时间会消耗 a a a格电,你也可以选择消耗 b b b格电将手机关机,而开机不需要消耗电量,同时开关机所需的时间忽略不计。

在0时刻时手机处于开机状态,能否发完这 n n n封邮件。

分析:

实际上只需要考虑发相邻两封邮件之间所需的时间,如果开机耗电更少就选择开机,开机耗电更大就选择关机,统计所需的总电量,再与开始时的电量比较,如果足够(开始电量大于消耗电量),那么就可以完成,否则就是不能完成。

坑点:

要注意计算耗电量时会超过int范围,需要使用long long类型存储数据。

代码:

#include<bits/stdc++.h>using namespace std;
typedef long long ll;
const int N = 2e5 + 5e2;ll n, f, a, b, m[N];void solve() {cin >> n >> f >> a >> b;for (int i = 1; i <= n; i++) {cin >> m[i];if (f <= 0) continue;if ((m[i] - m[i - 1]) * a > b) {f -= b;} else {f -= (m[i] - m[i - 1]) * a;}}if (f > 0) cout << "Yes" << endl;else cout << "No" << endl;
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

D.Very Different Array(贪心)

题意:

给出一个包含 n n n个数字的序列 A = a 1 , a 2 , . . . , a n A = a_1, a_2, ..., a_n A=a1,a2,...,an和一个包含 m ( m ≥ n ) m(m \ge n) m(mn)个数字的序列 B = b 1 , b 2 , . . . , b m B = b_1, b_2, ..., b_m B=b1,b2,...,bm,你可以从序列 B B B中取出 n n n个数字并任意重排获得序列 C = c 1 , c 2 , . . . , c n C = c_1, c_2, ..., c_n C=c1,c2,...,cn,求最大的 D = ∑ i = 1 n ∣ a i − c i ∣ D = \sum\limits_{i = 1}^{n}|a_i - c_i| D=i=1naici

分析:

先简化问题,思考从 A A A中取出一个数字 x x x B B B中取出一个数字 y y y,该怎么得到最大的 ∣ x − y ∣ |x - y| xy,不难想到,答案会是以下两种情况之一:

  1. x x x A A A中最大的数字, y y y B B B中最小的数字。

  2. x x x A A A中最小的数字, y y y B B B中最大的数字。

基于这个贪心的思路,先将输入的 A , B A, B A,B序列进行排序,并使用双指针维护这两个序列中最大最小的数字,并按以上思路进行取数,每次选择两个方案中较大的一个,序列 A A A中所有数字均被取完后就得到了最大的 D D D

代码:

#include<bits/stdc++.h>using namespace std;
typedef long long ll;
const int N = 2e5 + 5e2;ll n, m, a[N], b[N];void solve() {cin >> n >> m;for (int i = 1; i <= n; i++) cin >> a[i];for (int j = 1; j <= m; j++) cin >> b[j];sort(a + 1, a + n + 1); sort(b + 1, b + m + 1);ll ans = 0;int la = 1, ra = n, lb = 1, rb = m;while (la <= ra) {if (abs(a[la] - b[rb]) >= abs(a[ra] - b[lb])) {ans += abs(a[la++] - b[rb--]);} else {ans += abs(a[ra--] - b[lb++]);}}cout << ans << endl;
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

E.Eat the Chip(思维)

题意:

有一个 n × m n \times m n×m的棋盘,AliceBob各有一枚棋子,分别在棋盘上的 ( x a , y a ) , ( x b , y b ) (x_a, y_a), (x_b, y_b) (xa,ya),(xb,yb)坐标上。

Alice作为先手,Bob作为后手。

Alice每轮操作可以选择将棋子移动到 ( x a + 1 , y a ) , ( x a + 1 , y a + 1 ) , ( x a + 1 , y a − 1 ) (x_a + 1, y_a), (x_a + 1, y_a + 1), (x_a + 1, y_a - 1) (xa+1,ya),(xa+1,ya+1),(xa+1,ya1)三个位置中的一个。

Bob每轮操作可以选择将棋子移动到 ( x b − 1 , y b ) , ( x b − 1 , y b + 1 ) , ( x b − 1 , y b − 1 ) (x_b - 1, y_b), (x_b - 1, y_b + 1), (x_b - 1, y_b - 1) (xb1,yb),(xb1,yb+1),(xb1,yb1)三个位置中的一个。

如果某个选手的棋子可以在移动后到达另一个棋子的位置上,那么另一个选手的棋子就被吃掉了,该选手就赢下了这场比赛。

问:谁可以赢下这场比赛?

分析:

首先在开始时就可以思考平局的情况,由于Alice的棋子只能向下移动,Bob的棋子只能向下移动,那么如果开始时Alice的棋子的位置已经在Bob的棋子同一层或是更低一层的位置上时,双方一定无法吃掉对方的棋子,此时必然平局。

然后考虑双方棋子之间差了多少行,如果差的行数是奇数,那么此时必然为Alice操作,那么只有Alice可能会胜利,反之,如果差的行数为偶数,那么只有Bob可能会获得胜利。

然后考虑当某一位选手可能胜利时,双方各自会选择什么策略,这里以Alice可能胜利的情况为例:

  • Alice想要胜利,那么必然会选择让棋子不断靠近 B o b Bob Bob的棋子所在的那一列

  • Bob不想输掉比赛,那么必然会选择朝Alice棋子所在的列的另一个方向移动,即不断远离Alice的棋子,当到达边界后,选择不断向正下方移动。

  • 记双方棋子之间的列差为 l e n len len, 由于Alice想要追上Bob需要等Bob停留在边界上,那么实际上Alice需要的移动次数即为Alice距靠近Bob的棋子所在的边界的距离,这个距离记为 s t e p step step,而Alice是先手,那么此时Bob的移动次数是少一的,即移动次数为 s t e p − 1 step - 1 step1,那么此时双方的移动次数之和为 2 × s t e p − 1 2 \times step - 1 2×step1,只要 l e n ≥ 2 × s t e p − 1 len \ge 2 \times step - 1 len2×step1,那么之后无论Bob怎么移动,Alice都可以跟随Bob的操作追上Bob赢下比赛。

  • 如果列差不够Alice追上Bob,那么就是平局。

对于Bob,如果想要获胜,条件为 l e n ≥ 2 × s t e p len \ge 2 \times step len2×step

特殊情况:

l e n len len为正整数时,有以下两种特殊情况:

  • len为奇数,且AliceBob的棋子在同一列或相邻两列时,此时Alice必胜。

  • len为偶数,且且AliceBob的棋子在同一列时,此时Bob必胜。

代码:

#include<bits/stdc++.h>using namespace std;
typedef long long ll;
const int N = 2e5 + 5e2;int n, m, ax, ay, bx, by;void solve() {cin >> n >> m >> ax >> ay >> bx >> by;int len = bx - ax;if (len <= 0) {cout << "Draw" << endl;return;} if (len & 1) {if (abs(ay - by) <= 1) {cout << "Alice" << endl;return;}int step = 0;if (ay > by) {step = ay - 1;} else {step = m - ay;}if (step * 2 - 1 <= len) {cout << "Alice" << endl;} else {cout << "Draw" << endl;}} else {if (abs(ay - by) == 0) {cout << "Bob" << endl;return;}int step = 0;if (ay > by) {step = m - by;} else {step = by - 1;}if (step * 2 <= len) {cout << "Bob" << endl;} else {cout << "Draw" << endl;}}
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

F.Sum of Progression(根号分治,前缀和)

题意:

给出一个包含 n n n个数字的数组 a a a,有 q q q个询问,每个询问会给出三个数字 s , d , k s, d, k s,d,k,请你回答 a s + a s + d ⋅ 2 + . . . + a s + d ⋅ ( k − 1 ) ⋅ k a_s + a_{s + d} \cdot 2 + ... + a_{s + d \cdot (k - 1)} \cdot k as+as+d2+...+as+d(k1)k是多少。

分析:

如果使用循环来进行计算,那么对于由于 d × k ≤ n d \times k \le n d×kn,如果给出的 d d d比较大,那么要计算的总数 k k k就会很小,因此,可以选择接近 n \sqrt{n} n 的一个数字作为分界点(这里选了333),当输入的 d d d大于等于分界点时,就使用循环来直接计算得到答案。

然后对于 d d d较小的情况,可以维护两种前缀和:

  • p r e [ 1 ] [ d ] [ j ] pre[1][d][j] pre[1][d][j]表示 a j − m ⋅ d + 2 ⋅ a j − ( m − 1 ) ⋅ d + . . . + ( m + 1 ) ⋅ a j a_{j - m \cdot d} + 2 \cdot a_{j - (m - 1) \cdot d} + ... + (m + 1) \cdot a_{j} ajmd+2aj(m1)d+...+(m+1)aj

  • p r e [ 0 ] [ d ] [ j ] pre[0][d][j] pre[0][d][j]表示 a j − m ⋅ d + a j − ( m − 1 ) ⋅ d + . . . + a j a_{j - m \cdot d} + a_{j - (m - 1) \cdot d} + ... + a_{j} ajmd+aj(m1)d+...+aj

其中, m m m表示的是从第 j j j个数字开始以 d d d为一步的距离,最多往前走 m m m次。

即预处理 1 ∼ 333 1 \sim 333 1333作为两个数之间的间隔,维护的前缀和。

然后通过前缀和就可以轻松计算得到要求的答案了。

例:

形如: a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an为以某个数字为间隔取出的数字, b 1 , b 2 , . . . b n b_1, b_2, ... b_n b1,b2,...bn分别等于 a 1 , a 1 + a 2 , . . . , ∑ i = 1 n a i a_1, a_1 + a_2, ..., \sum\limits_{i = 1}^{n}a_i a1,a1+a2,...,i=1nai c 1 , c 2 , . . . , c n c_1, c_2, ..., c_n c1,c2,...,cn分别等于 a 1 , a 1 + 2 ⋅ a 2 , . . . , ∑ i = 1 n i ⋅ a i a_1, a_1 + 2 \cdot a_2, ..., \sum\limits_{i = 1}^{n} i \cdot a_i a1,a1+2a2,...,i=1niai

当想要知道此时 ∑ i = l r ( i − l + 1 ) ⋅ a i \sum\limits_{i = l}^{r}(i - l + 1) \cdot a_i i=lr(il+1)ai时,可以通过前缀和先得到 p ⋅ a l + ( p + 1 ) ⋅ a l + 1 + . . . + ( p + k − 1 ) ⋅ a r p \cdot a_l + (p + 1) \cdot a_{l + 1} + ... + (p + k - 1) \cdot a_r pal+(p+1)al+1+...+(p+k1)ar,然后通过另一个前缀和减去 p − 1 p - 1 p1 ∑ i = l r a i \sum\limits_{i = l}^{r}a_i i=lrai即可。

代码:

#include<bits/stdc++.h>using namespace std;
typedef long long ll;
const int N = 1e5 + 5e2;int n, q, s, d, k;
ll a[N], pre[2][350][N];void init() {for (int i = 1; i <= n && i <= 333; i++) {//枚举间隔for (int j = 1; j <= n; j++) {//计算间隔为i的前缀和pre[0][i][j] = a[j];pre[1][i][j] = ((j - 1) / i + 1) * a[j];if (j > i) {pre[0][i][j] += pre[0][i][j - i];pre[1][i][j] += pre[1][i][j - i];}}}
}ll query_1() {//d <= 333//注意此时前缀和两个数之间的间隔为dll ans = pre[1][d][s + (k - 1) * d] - pre[1][d][max(0, s - d)];ll sub = (s - 1) / d * (pre[0][d][s + (k - 1) * d] - pre[0][d][max(0, s - d)]);return ans - sub;
}ll query_2() {// d > 333ll ans = 0;for (int i = 0; i < k; i++) {ans += (i + 1) * a[i * d + s];}return ans;
}void solve() {cin >> n >> q;for (int i = 1; i <= n; i++) cin >> a[i];init();while (q--) {cin >> s >> d >> k;if (d <= 333) {cout << query_1();} else {cout << query_2();}if (q == 0) cout << endl;else cout << ' ';}
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

G.Mischievous Shooter(分治,枚举)

题意:

Shel有一把特殊的武器,可以在击中一个目标点后同时击中四方向(右上,右下,左下,左上)中其中一个里的所有曼哈顿距离在 k k k以内的点。

给出一个 n × m n \times m n×m的靶子,靶子上有若干个得分点,问使用这把武器最多可以击中多少个得分点。

分析:

题目关键信息: n × m ≤ 1 0 5 n \times m \le 10^{5} n×m105可知 m i n ( n , m ) ≤ 1 0 5 min(n, m) \le \sqrt{10^{5}} min(n,m)105

因此,可以枚举所有点作为被击中的目标点,对于目标点的四种方案的检查,可以根据 n n n m m m谁较小,就循环谁去检查。

假如循环检查的是行,为了避免再通过循环去检查列上存在多少个得分点,需要对于每一行均维护一个前缀和,同理,对于每一列也需要维护一个前缀和。

记录枚举过程中最大的答案即可。

代码:

#include<bits/stdc++.h>using namespace std;
typedef long long ll;
const int N = 1e5 + 5e2;int n, m, k;string s[N];void solve() {cin >> n >> m >> k;//row[i][j]表示第i行的第1~j列的数字总和//col[i][j]表示第i列的第1~j行的数字总和vector<vector<int> > row(n + 1, vector<int>(m + 1, 0)),col(m + 1, vector<int>(n + 1, 0));for (int i = 1; i <= n; i++) {cin >> s[i];for (int j = 1; j <= m; j++) {int a = (s[i][j - 1] == '#');row[i][j] = row[i][j - 1] + a;}}for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {int a = (s[j][i - 1] == '#');col[i][j] = col[i][j - 1] + a;}}int ans = 0;if (n < m) {//枚举行for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {int sum = 0;for (int h = i, len = k; h >= i - k && h >= 1; h--, len--) {sum += row[h][min(m, j + len)] - row[h][j - 1];}ans = max(ans, sum);sum = 0;for (int h = i, len = k; h <= i + k && h <= n; h++, len--) {sum += row[h][min(m, j + len)] - row[h][j - 1];}ans = max(ans, sum);sum = 0;for (int h = i, len = k + 1; h <= i + k && h <= n; h++, len--) {sum += row[h][j] - row[h][max(0, j - len)];}ans = max(ans, sum);sum = 0;for (int h = i, len = k + 1; h >= i - k && h >= 1; h--, len--) {sum += row[h][j] - row[h][max(0, j - len)];}ans = max(ans, sum);}}} else {//枚举列for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {int sum = 0;for (int w = j, len = k + 1; w <= j + k && w <= m; w++, len--) {sum += col[w][i] - col[w][max(0, i - len)];}ans = max(ans, sum);sum = 0;for (int w = j, len = k; w <= j + k && w <= m; w++, len--) {sum += col[w][min(n, i + len)] - col[w][i - 1];}ans = max(ans, sum);sum = 0;for (int w = j, len = k; w >= j - k && w >= 1; w--, len--) {sum += col[w][min(n, i + len)] - col[w][i - 1];}ans = max(ans, sum);sum = 0;for (int w = j, len = k + 1; w >= j - k && w >= 1; w--, len--) {sum += col[w][i] - col[w][max(0, i - len)];}ans = max(ans, sum);}}}cout << ans << endl;
}int main() {int Case;cin >> Case;while (Case--) {solve();}return 0;
}

学习交流

以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。

这篇关于Codeforces Round 920 (Div. 3) A~G的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/618230

相关文章

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

Codeforces 482B 线段树

求是否存在这样的n个数; m次操作,每次操作就是三个数 l ,r,val          a[l] & a[l+1] &......&a[r] = val 就是区间l---r上的与的值为val 。 也就是意味着区间[L , R] 每个数要执行 | val 操作  最后判断  a[l] & a[l+1] &......&a[r] 是否= val import ja

CSS实现DIV三角形

本文内容收集来自网络 #triangle-up {width: 0;height: 0;border-left: 50px solid transparent;border-right: 50px solid transparent;border-bottom: 100px solid red;} #triangle-down {width: 0;height: 0;bor

创建一个大的DIV,里面的包含两个DIV是可以自由移动

创建一个大的DIV,里面的包含两个DIV是可以自由移动 <body>         <div style="position: relative; background:#DDF8CF;line-height: 50px"> <div style="text-align: center; width: 100%;padding-top: 0px;"><h3>定&nbsp;位&nbsp;

Codeforces Round 971 (Div. 4) (A~G1)

A、B题太简单,不做解释 C 对于 x y 两个方向,每一个方向至少需要 x / k 向上取整的步数,取最大值。 由于 x 方向先移动,假如 x 方向需要的步数多于 y 方向的步数,那么最后 y 方向的那一步就不需要了,答案减 1 代码 #include <iostream>#include <algorithm>#include <vector>#include <string>

CF#271 (Div. 2) D.(dp)

D. Flowers time limit per test 1.5 seconds memory limit per test 256 megabytes input standard input output standard output 题目链接: http://codeforces.com/contest/474/problem/D We s

CF #278 (Div. 2) B.(暴力枚举+推导公式+数学构造)

B. Candy Boxes time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output 题目链接: http://codeforces.com/contest/488/problem/B There