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(贪心)

题意:

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

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

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

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

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

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

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

给出目前盒子的状态序列:s1,...,sns_1, ..., s_ns1,...,sn,问将该序列修改为目标序列:f1,...,fnf_1, ..., f_nf1,...,fn至少需要几次操作。

分析:

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

定义addaddadd为序列中si=0s_i = 0si=0fi=1f_i = 1fi=1的个数,subsubsub为序列中si=1s_i = 1si=1fi=0f_i = 0fi=0的个数。

那么可以使用的操作3的次数即为min(add,sub)min(add, sub)min(add,sub),同时不难发现,剩余所需的操作为∣add−sub∣|add - sub|addsub,因此实际所需的总操作次数为max(add,sub)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(贪心)

题意:

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

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

分析:

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

坑点:

要注意计算耗电量时会超过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(贪心)

题意:

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

分析:

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

  1. xxxAAA中最大的数字,yyyBBB中最小的数字。

  2. xxxAAA中最小的数字,yyyBBB中最大的数字。

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

代码:

#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×mn \times mn×m的棋盘,AliceBob各有一枚棋子,分别在棋盘上的(xa,ya),(xb,yb)(x_a, y_a), (x_b, y_b)(xa,ya),(xb,yb)坐标上。

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

Alice每轮操作可以选择将棋子移动到(xa+1,ya),(xa+1,ya+1),(xa+1,ya−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每轮操作可以选择将棋子移动到(xb−1,yb),(xb−1,yb+1),(xb−1,yb−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想要胜利,那么必然会选择让棋子不断靠近BobBobBob的棋子所在的那一列

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

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

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

对于Bob,如果想要获胜,条件为len≥2×steplen \ge 2 \times steplen2×step

特殊情况:

lenlenlen为正整数时,有以下两种特殊情况:

  • 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(根号分治,前缀和)

题意:

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

分析:

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

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

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

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

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

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

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

例:

形如:a1,a2,...,ana_1, a_2, ..., a_na1,a2,...,an为以某个数字为间隔取出的数字,b1,b2,...bnb_1, b_2, ... b_nb1,b2,...bn分别等于a1,a1+a2,...,∑i=1naia_1, a_1 + a_2, ..., \sum\limits_{i = 1}^{n}a_ia1,a1+a2,...,i=1naic1,c2,...,cnc_1, c_2, ..., c_nc1,c2,...,cn分别等于a1,a1+2⋅a2,...,∑i=1ni⋅aia_1, a_1 + 2 \cdot a_2, ..., \sum\limits_{i = 1}^{n} i \cdot a_ia1,a1+2a2,...,i=1niai

当想要知道此时∑i=lr(i−l+1)⋅ai\sum\limits_{i = l}^{r}(i - l + 1) \cdot a_ii=lr(il+1)ai时,可以通过前缀和先得到p⋅al+(p+1)⋅al+1+...+(p+k−1)⋅arp \cdot a_l + (p + 1) \cdot a_{l + 1} + ... + (p + k - 1) \cdot a_rpal+(p+1)al+1+...+(p+k1)ar,然后通过另一个前缀和减去p−1p - 1p1∑i=lrai\sum\limits_{i = l}^{r}a_ii=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
    //注意此时前缀和两个数之间的间隔为d
    ll 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 > 333
    ll 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有一把特殊的武器,可以在击中一个目标点后同时击中四方向(右上,右下,左下,左上)中其中一个里的所有曼哈顿距离在kkk以内的点。

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

分析:

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

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

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

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

代码:

#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 927 Div. 3 比赛详情 Codeforces是一个面向全球程序员的比赛平台,定期举办不同级别的编程竞赛。Div. 3系列比赛专为评级较低的选手设计,旨在提供更简单的问题让新手能够参与并提升技能[^1]。 #### 参赛规则概述 这类赛事通常允许单人参加,在规定时间内解决尽可能多的问题来获得分数。评分机制基于解决问题的速度以及提交答案的成功率。比赛中可能会有预测试案例用于即时反馈,而最终得分取决于系统测试的结果。此外,还存在反作弊措施以确保公平竞争环境。 ### 题目解析:Moving Platforms (G) 在这道题中,给定一系列移动平台的位置和速度向量,询问某时刻这些平台是否会形成一条连续路径使得可以从最左端到达最右端。此问题涉及到几何学中的线段交集判断和平面直角坐标系内的相对运动分析。 为了处理这个问题,可以采用如下方法: - **输入数据结构化**:读取所有平台的数据,并将其存储在一个合适的数据结构里以便后续操作。 - **时间轴离散化**:考虑到浮点数精度误差可能导致计算错误,应该把整个过程划分成若干个小的时间间隔来进行模拟仿真。 - **碰撞检测算法实现**:编写函数用来判定任意两个矩形之间是否存在重叠区域;当发现新的连接关系时更新可达性矩阵。 - **连通分量查找技术应用**:利用图论知识快速求解当前状态下哪些节点属于同一个集合内——即能否通过其他成员间接相连。 最后输出结果前记得考虑边界条件! ```cpp // 假设已经定义好了必要的类和辅助功能... bool canReachEnd(vector<Platform>& platforms, double endTime){ // 初始化工作... for(double currentTime = startTime; currentTime <= endTime ;currentTime += deltaT){ updatePositions(platforms, currentTime); buildAdjacencyMatrix(platforms); if(isConnected(startNode,endNode)){ return true; } } return false; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值