USACO 2024年1月比赛 铜组题解


比赛链接http://usaco.org/

第一题:MAJORITY OPINION

标签:思维、模拟
题意:给定一个长度为 n n n的序列 a a a,操作:若区间 [ i , j ] [i,j] [i,j]内某个数字 k k k出现的次数 大于区间长度的一半,可以将区间内的所有数都换成这个数 k k k。经过多次操作之后,让区间 [ 1 , n ] [1,n] [1,n]内都为同一个数,输出所有可能的数(按照数字递增的顺序),若没有输出 − 1 -1 1。( 1 < = n < = 1 0 5 , 1 < = a i < = n 1<=n<=10^5,1<=a_i<=n 1<=n<=105,1<=ai<=n
题解:连续两个相同的数,不管是往前还是往后一个,都可以把加进来的数变成区间内的这个数。
比如 x   y   y   z x\ y \ y \ z x y y z可以往前把 x x x变成 y y y,往后把 z z z变成 y y y,那其实再往前或者再往后 可以把所有数都变成 y y y
除此之外,还有 y   x   y y \ x\ y y x y的情况也是满足条件的,先把中间的这个 x x x变成 y y y,然后也可以把所有数都变成 y y y

最终就变成了求第 i i i数和 i − 1 i-1 i1个数或者第 i − 2 i-2 i2个数 是否相同,相同的话,就可以去作为我们的答案,当然可能有重复的情况,所以要去重输出。
代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int t, n, a[N], res[N];

int main() {
    cin >> t;
    while (t--) {
        cin >> n;
        int c = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (i >= 2 && a[i] == a[i - 1]) res[++c] = a[i];
            else if (i >= 3 && a[i] == a[i - 2]) res[++c] = a[i];
        }
        sort(res + 1, res + 1 + c);
        c = unique(res + 1, res + 1 + c) - res - 1;
        if (c == 0) cout << -1 << endl;
        else {
            for (int i = 1; i < c; i++) {
                cout << res[i] << " ";
            }
            cout << res[c] << endl;
        }
    }
    return 0;
}

第二题:CANNONBALL

标签:模拟
题意:给定一个从左往右 1 , 2 , . . . , N 1,2,...,N 1,2,...,N标记的数轴,从其中某个位置 S S S开始往右跳,初始能量为 1 1 1。如果当前能量为 k k k,将会从当前位置往前跳 k k k的距离。
每个标记的位置,要么是跳板,要么是靶子,每个位置都有值 v i v_i vi
跳板:跳到跳板上,当这个位置的值是 v i v_i vi,当前能量会增加 v i v_i vi,并反转跳的方向。
靶子:跳到靶子上,如果当前能量大于等于 当前位置的值 v i v_i vi,将会打破靶子,靶子被破坏掉后,将会一直维持破坏的状态,下次跳到这个位置无法被打破。
假设可以跳无限长的时间,或者离开数轴位置,能打破多少个靶子?( 1 < = N < = 1 0 5 1<=N<=10^5 1<=N<=105
题解:维持当前朝向 d i r dir dir,当前能量 e n e r g y energy energy,模拟跳的过程,当跳的过程中 s < 1 s<1 s<1或者 s > n s>n s>n结束。每次按照规则进行模拟跳,跳板的时候增加能量值,反转跳的方向,打靶的时候标记一下靶子是否被破坏过。

如果在同一个能量值跳到 某个位置 两次,那么可以得到以后的循环不会有新的靶子被破坏,再之后就是重复新的一轮了。小估估一下时间复杂度,让它尽量跳,超出轮数默认不会再产生新的打破靶子数。
代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
int n, s, q[N], v[N], ans = 0;
bool vis[N];

int main() {
    cin >> n >> s;
    for (int i = 1; i <= n; i++) {
        cin >> q[i] >> v[i];
    }
    int energy = 1, dir = 1; // 能量、朝向
    for (int i = 1; i <= 1000 * N; i++) {
        if (q[s] == 0) { // 跳板
            energy += v[s];
            dir *= -1;
        }
        else { // 靶子
            if (!vis[s] && energy >= v[s]) {
                vis[s] = 1;
                ans++;
            }
        }
        s += dir * energy;
        if (s < 1 || s > n) break;
    }
    cout << ans << endl;
    return 0;
}

第三题:BALANCING BACTERIA

标签:思维、差分
题意:给定 n n n个数, a 1 , a 2 , a 3 . . . a n a_1,a_2,a_3...a_n a1,a2,a3...an,每次操作 可以选择数字 L ( 1 < = L < = n ) L(1<=L<=n) L1<=L<=n,并选择增加或者减少。
比如增加的情况,从第 n n n个数开始,第 n n n个数增加 L L L,第 n − 1 n-1 n1个数增加 L − 1 L-1 L1,第 n − 2 n-2 n2个数增加 L − 2 L-2 L2…,依次类推,直到增加值为 0 0 0,再往前就不增加(即第 1 1 1个到第 n − L n-L nL个数不增加值)
求将所有数变成 0 0 0的最少操作次数。( 1 < = n < = 2 ∗ 1 0 5 , 1 0 − 15 < = a i < = 1 0 15 1<=n<=2*10^5,10^{-15}<=a_i<=10^{15} 1<=n<=2105,1015<=ai<=1015

举个例子:比如有两个数 − 1     3 -1\ \ \ 3 1   3
可以先从选择数字 L = 1 L=1 L=1并进行减少,操作 5 5 5次,把序列变成: − 1      − 2 -1\ \ \ \ -2 1    2
然后选择数字 L = 2 L=2 L=2,并进行增加,操作 1 1 1次,把序列变成: 0     0 0\ \ \ 0 0   0。总共 6 6 6次操作。
题解:以样例 2 2 2为例,有 5 5 5个数: 1     3    − 2    − 7     5 1\ \ \ 3 \ \ -2 \ \ -7 \ \ \ 5 1   3  2  7   5
我们维护一个差分数组 b i b_i bi a i − a i − 1 a_i-a_{i-1} aiai1 1     2    − 5    − 5     12 1\ \ \ 2 \ \ -5 \ \ -5 \ \ \ 12 1   2  5  5   12
这个差分数组表示原 a a a数组中相邻两个数的差值,我们额外对 i = 1 i=1 i=1的时候求 b 1 = a 1 − a 0 b_1=a_1-a_0 b1=a1a0 a 0 = 0 a_0=0 a0=0
再对差分数组 b i b_i bi做差分, c i = b i − b i − 1 c_i=b_i-b_{i-1} ci=bibi1 1    1    − 7    0    17 1 \ \ 1 \ \ -7 \ \ 0 \ \ 17 1  1  7  0  17

a i a_i ai: 1     3    − 2    − 7     5 1\ \ \ 3 \ \ -2 \ \ -7 \ \ \ 5 1   3  2  7   5
1 1 1 a i a_i ai − 1   − 2   − 3   − 4   − 5 = > 0     1    − 5    − 11     0 -1\ -2\ -3\ -4 \ -5=> 0\ \ \ 1 \ \ -5 \ \ -11 \ \ \ 0 1 2 3 4 5=>0   1  5  11   0 (操作次数 + 1 +1 +1
2 2 2 a i a_i ai 0   − 1   − 2   − 3   − 4 = > 0     0    − 7    − 14     − 4 0\ -1\ -2\ -3 \ -4=> 0\ \ \ 0 \ \ -7 \ \ -14 \ \ \ -4 0 1 2 3 4=>0   0  7  14   4 (操作次数 + 1 +1 +1
3 3 3 a i a_i ai 0   0   1   2   3 = > 0     0    0    0     17 0\ 0\ 1\ 2 \ 3=> 0\ \ \ 0 \ \ 0 \ \ 0 \ \ \ 17 0 0 1 2 3=>0   0  0  0   17 (操作次数 + 7 +7 +7
4 4 4 a i a_i ai 0   0   0   0   0 = > 0     0    0    0     17 0\ 0\ 0\ 0 \ 0=> 0\ \ \ 0 \ \ 0 \ \ 0 \ \ \ 17 0 0 0 0 0=>0   0  0  0   17 (操作次数 + 0 +0 +0
5 5 5 a i a_i ai 0   0   0   0   − 1 = > 0     0    0    0     0 0\ 0\ 0\ 0 \ -1=> 0\ \ \ 0 \ \ 0 \ \ 0 \ \ \ 0 0 0 0 0 1=>0   0  0  0   0 (操作次数 + 17 +17 +17

我们能够发现每次归 0 0 0的操作次数就是对应 c i c_i ci的绝对值。其实就是对 b i b_i bi进行差分,得到每个对应的后缀需要修改多少。
代码

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10;
typedef long long ll;
ll a[N], b[N], n, ans = 0;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        b[i] = a[i] - a[i - 1]; // 差分
    }
    for (int i = 1; i <= n; i++) {
        ans += abs(b[i] - b[i - 1]);
    }
    cout << ans << endl;
    return 0;
}
  • 42
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
USACO2022金组是国际在线判题系统USACO的最高级别,题目难度较高,在该比赛中取得好成绩是一项巨大的成就。以下是对该比赛的一些题目解析。 第一题:“交通计划” 题目要求:给定一个n个节点的有向图,每条边有一个长度,希望添加最少的边使得所有节点连通,求最小生成树的权值和。 解析:该题可以使用Kruskal算法求解,将每条边按权值从小到大排序,再依次加入,判断加入的边是否会形成环,若形成则不加入,直到所有节点连通为止。此时Kruskal算法得到的最小生成树的权值和即为所求。 第二题:“点火计划” 题目要求:给定一个n个节点的有向图,每条边有一个权值和一个点火时长,每个节点有一个点火启动时刻和时刻结束时刻,希望从其中选出一些边点火,使得所有节点都可从点火的边出发到达,且所选点火边的总点火时长最小。 解析:该题可以使用最小费用最大流算法求解。将每条边看做一个容量为1,费用为点火时长的边,源点向节点的点火边容量为1,费用为0的边,节点的点火边向汇点的容量为1,费用为0的边,对这个网络进行最小费用最大流即可得到所选边的总点火时长最小。 第三题:“美味佳肴” 题目要求:给定n个菜品,每个菜品有它的权值和两个类别,希望选出k个菜品,使得选出的菜品数量在每个类别中都不超过$\frac{k}{3}$个,且所选菜品的权值和最大。 解析:该题可以使用动态规划求解。设$f[i][j][k]$表示前i个菜品中,选择j个一类菜品,选择k个二类菜品的最大权值和,状态转移方程为$f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][k]+a[i],f[i-1][j][k-1]+b[i])$,其中a[i]为i号菜品的权值,若为一类则为该权值,否则为0,b[i]为i号菜品的权值,若为二类则为该权值,否则为0。最终答案为$f[n][$k/3$][$k/3$]。 以上是对USACO2022金组的部分题目的解析,USACO比赛是全球范围内的计算机竞赛,竞争非常激烈,能够在该比赛中脱颖而出是一项非常棒的成就。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值