USACO 2024年2月比赛 铜组题解

U S A C O    2 USACO \ \ 2 USACO  2月比赛 铜组题解
比赛链接http://usaco.org/

第一题:PALINDROME GAME

标签:思维、博弈
题意:给定一堆石子,有 S S S 个( 1 ≤ S ≤ 1 0 1 0 15 1≤S≤10^{10^{15}} 1S101015), B e s s i e Bessie Bessie E l s i e Elsie Elsie轮流拿回文正整数个石子, B e s s i e Bessie Bessie先手,当轮到一个人的时候 石子堆空了,该人输。两个人都执行最优策略,求谁会赢?
题解 S S S数据很大,显然不能直接暴力模拟。我们可以找几个数据,推一推。
一位数的情况, B e s s i e Bessie Bessie直接拿走获胜。
两位数的情况,比如 10 10 10 B e s s i e Bessie Bessie至少得拿一个,不管拿多少个,剩下来都是一位数, E l s i e Elsie Elsie赢。 11 、 12 、 13...19 11、12、13...19 111213...19的情况, B e s s i e Bessie Bessie可以把个位直接拿走,留 10 10 10 E l s i e Elsie Elsie B e s s i e Bessie Bessie赢。
具体再看看 S = 119 S=119 S=119之类的,发现只要 B e s s i e Bessie Bessie把个位拿走,留 110 110 110 E l s i e Elsie Elsie,也是必胜的,只要 E l s i e Elsie Elsie更改, B e s s i e Bessie Bessie把个位多余的拿走,最后就能留 10 10 10 E l s i e Elsie Elsie
推了下,我们发现个位为 0 0 0的为必败态,所以最终只要判 S S S个位是否为 0 0 0,就能知道是谁赢了。
代码

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

int main() {
    int t;
    cin >> t;
    while (t--) {
        string s;
        cin >> s;
        int n = s.size();
        if (s[n - 1] != '0') cout << "B" << endl;
        else cout << "E" << endl;
    }
    return 0;
}

第二题:MILK EXCHANGE

标签:思维、倍增
题意:给定 N N N个数围成环。第 i i i个右边是第 i + 1 i+1 i+1,左边是第 i − 1 i-1 i1。特殊的是第 1 1 1个数 左边是第 N N N个数,第 N N N个数右边是第 1 1 1个数。初始 第 i i i个数为 a i a_i ai。给定一个字符串 s 1 , s 2 , . . . s n s_1,s_2,...s_n s1,s2,...sn,对于第 i i i个字符 s i s_i si要么为 L L L,要么为 R R R。对于 s i = L s_i=L si=L的情况下, a i a_i ai每分钟传递 1 1 1的值给左边;对于 s i = R s_i=R si=R的情况下, a i a_i ai每分钟传递 1 1 1的值给右边。传递都是同时进行的。
传递的过程如果如果值超过了 初始的 a i a_i ai 将会溢出,求经过 M M M分钟之后 这些所有数值之和为多少。
1 ≤ N ≤ 2 ∗ 1 0 5 , 1 ≤ M , a i ≤ 1 0 9 1 ≤ N ≤ 2 * 10^5,1 ≤ M,a_i ≤ 10^9 1N21051Mai109
题解:数据很大,模拟每一分钟肯定不现实。我们先观察下一个例子

4 1
RRLL
1 1 1 1

初始 a i a_i ai都是 1 1 1,求第 1 1 1分钟之后 数值之和。
1 1 1分钟的时候, 1 1 1 2 2 2流, 2 2 2 3 3 3流; 4 4 4 3 3 3流, 3 3 3 2 2 2流。我们能发现对于第 2 2 2个和第 3 3 3个来说是没有损失的。但是对于第 1 1 1个和第 4 4 4个来说损失了 1 1 1

推一推能看出来,对于 R R R R L L L L L L RRRRLLLLLL RRRRLLLLLL的,左边连续 R R R和右边连续 L L L都是流向中间 R L RL RL的地方,然后分别溢出的。那溢出多少呢?是不是和时间 M M M相关。那我们其实就去把所有的 R L RL RL找到,然后往左和往右,两边连续的 R R R L L L分别溢出 M M M减去,再求下和即可。注意特判一下全 L L L和全 R R R的情况。
代码

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

typedef long long ll;
const ll N = 2e5 + 10;
string s;
ll cntl = 0, cntr = 0;
ll a[N], n, m, ans = 0, sum = 0;

int main() {
    cin >> n >> m;
    cin >> s;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
        if (s[i - 1] == 'L') cntl++;
        else cntr++;
        a[i + n] = a[i + 2*n] = a[i]; // 倍增
    }

    if (cntl == n || cntr == n) {
        cout << sum << endl;
        return 0;
    }

    s = "@" + s + s + s; // 环 倍增处理
    for (int i = 1; i <= n; i++) {
        if (s[i] == 'R' && s[i + 1] == 'L') {
            ll l = i + n, r = i + 1 + n;
            ll suml = 0, sumr = 0;
            while (l > 1 && s[l - 1] == 'R') {
                l--;
                suml += a[l];
            }
            while (r < 3 * n && s[r + 1] == 'L') {
                r++;
                sumr += a[r];
            }
            // m轮之后 左边连续L剩余的 + 右边连续R剩余的
            ll leave = max(suml - m, 0ll) + max(sumr - m, 0ll);
            ans += a[i] + a[i + 1] + leave; // i和i+1位置牛奶量不变保留
        }
    }
    cout << ans << endl;
    return 0;
}

第三题:MAXIMIZING PRODUCTIVITY

标签:二分查找、思维
题意:给定 N N N个农场,第 i i i个农场在 c i c_i ci的时候关闭。 B e s s i e Bessie Bessie在时间 S S S的时候起床,计划在 t i + S t_i+S ti+S的时间访问第 i i i个农场,必须在农场关闭之前去访问。进行 Q Q Q次查询,每次给出 S S S V V V,求是否能在时间 S S S的时候起床,并访问至少 V V V个农场。
1 ≤ S , N ≤ 1 0 6 , 1 ≤ c i , t i ≤ 1 0 6 , 1 ≤ V < = N 1≤S,N≤10^6,1≤c_i,t_i≤10^6,1≤V<=N 1S,N1061ci,ti1061V<=N
二分查找解法 题解:因为查询次数比较多,直接暴力模拟 肯定会超时,我们需要做一些优化处理。题目中要求在农场关闭之前访问,所以我们可以把数组 c c c减去数组 t t t求个差值 再减一(因为题目要求关闭之前),求出第 i i i个如果要访问到,最迟的起床时间。然后对差值数组进行一下排序。
比如排序后的差值数组是: − 3 、 2 、 4 、 7 、 10 、 15 -3、2、4、7、10、15 32471015
假定当前查询的 S = 4 , V = 3 S=4,V=3 S=4V=3,那么可以通过二分找到第 3 3 3个位置,那么从这个位置往后到第 N N N个的位置都是可以访问到的,记 N − P + 1 N-P+1 NP+1个。( P P P表示通过 l o w e r _ b o u n d lower\_bound lower_bound函数找到的第一个大于等于 S S S的位置),然后我们与对应要求个数的 V V V对比判定一下输出即可。
二分查找解法 代码

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

const int N = 1e6 + 10;
int c[N], t[N], v, s, n, q;

int main() {
    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> c[i];
    for (int i = 1; i <= n; i++) {
        cin >> t[i];
        c[i] = c[i] - t[i] - 1;
    }
    sort(c + 1, c + 1 + n);
    while (q--) {
        cin >> v >> s;
        int p = lower_bound(c + 1, c + 1 + n, s) - c;
        if (n - p + 1 >= v) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

思维解法 题解:和二分查找解法类似,因为题目中要求在农场关闭之前访问,所以我们可以把数组 c c c减去数组 t t t求个差值,再减一(因为题目要求关闭之前),然后对差值数组进行一下从大到小排序。
比如排序后的差值数组是: 15 、 10 、 7 、 4 、 2 、 − 3 15、10、7、4、2、-3 15107423
假定当前查询的 S = 4 , V = 3 S=4,V=3 S=4V=3,判定 发现差值数组的第 3 3 3 个位置的值大于等于 S S S,那说明前 V V V个都能到达。如果同学们在做差值数组的时候没有减一,这边判大于就可以了。
思维解法 代码:

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

const int N = 1e6 + 10;
int c[N], t[N], v, s, n, q;

bool cmp(int x, int y) {
    return x > y;
}

int main() {
    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> c[i];
    for (int i = 1; i <= n; i++) {
        cin >> t[i];
        c[i] = c[i] - t[i] - 1;
    }
    sort(c + 1, c + 1 + n, cmp);
    while (q--) {
        cin >> v >> s;
        if (c[v] >= s) cout << "YES" << endl;
        else cout << "NO" << endl;
    }
    return 0;
}

  • 44
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值