2023牛客寒假算法基础集训营5(9/12)

小沙の好客

根据题意,尽可能多的挑选价值和x接近的数,使用前缀和和upper_bound快速计算

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, q;
    cin >> n >> q;
    vector<LL> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    sort(a.begin() + 1, a.end());
    vector<LL> sum(n + 1);
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i - 1] + a[i];
    }
    while (q--) {
        int k, x;
        cin >> k >> x;
        int idx = upper_bound(a.begin(), a.end(), x) - a.begin();
        if (idx >= k + 1) {
            cout << sum[idx - 1] - sum[idx - k - 1] << '\n';
        } else {
            cout << sum[idx - 1] << '\n';
        }
    }
    return 0;
}

小沙の博弈

可以发现的是,每个格子都放1个石子是最优的,所以最后只需要判断长度即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    if (n % 2 == 0) {
        cout << "win-win!\n";
    } else {
        cout << "Yaya-win!\n";
    }
    return 0;
}

小沙の不懂

首先容易想到,如果a和b长度相等,因为是0~9的排列,所以如果某种情况下a>b,那么一定对应着某种情况下b>a,所以a=b当且仅当a=b,否则a!=b,再考虑a和b长度不相等的时候,可以想到越长的可能越大,决定到底长的大还是短的大,取决于他们的相等的前缀最长是多少,如果a[0]==b[0],a的长度大于b的长度,且a去掉最长相等前缀后比b去掉最长相等前缀后要长,那么无论哪种映射关系,a一定比b大,同理b一定也会比a大,否则有的情况a>b,有的情况a<b,不能判断,那如果a[0]!=b[0],只需要考虑如果a的最长相等前缀全部映射成0的情况下,如果a比b长,那么a一定比b大,同理b一定比a大,否则无法判断

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    string a, b;
    cin >> a >> b;
    int len1 = a.size(), len2 = b.size();
    if (len1 == len2) {
        if (a == b) {
            cout << "=\n";
        } else {
            cout << "!\n";
        }
    } else {
        int cnt1 = 1, cnt2 = 1;
        for (int i = 1; i < len1; i++) {
            if (a[i] == a[i - 1]) {
                cnt1++;
            } else {
                break;
            }
        }
        for (int i = 1; i < len2; i++) {
            if (b[i] == b[i - 1]) {
                cnt2++;
            } else {
                break;
            }
        }
        if (a[0] == b[0]) {
            if (len1 - cnt1 > len2 - cnt2 && len1 > len2) {
                cout << ">\n";
            } else if (len1 - cnt1 < len2 - cnt2 && len1 < len2) {
                cout << "<\n";
            } else {
                cout << "!\n";
            }
        } else {
            if (len1 - cnt1 > len2) {
                cout << ">";
            } else if (len2 - cnt2 > len1) {
                cout << "<";
            } else {
                cout << "!\n";
            }
        }
    }
    return 0;
}

小沙の赌气

因为道具可以一直保存着,所以将没用的道具放在优先队列里,选取小根堆,即l越小越靠前,每回合判断能到第几关卡

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<pair<int, int>> a(n), b(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i].first >> a[i].second;
    }
    for (int i = 0; i < n; i++) {
        cin >> b[i].first >> b[i].second;
    }
    int nowa = 0, nowb = 0;
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> qa, qb;
    for (int i = 0; i < n; i++) {
        qa.push(a[i]);
        qb.push(b[i]);
        while (!qa.empty()) {
            if (qa.top().first <= nowa + 1) {
                nowa = max(nowa, qa.top().second);
                qa.pop();
            } else {
                break;
            }
        }
        while (!qb.empty()) {
            if (qb.top().first <= nowb + 1) {
                nowb = max(nowb, qb.top().second);
                qb.pop();
            } else {
                break;
            }
        }
        if (nowa == nowb) {
            cout << "win_win!\n0\n";
        } else if (nowa > nowb) {
            cout << "sa_win!\n";
            cout << nowa - nowb << '\n';
        } else if (nowa < nowb) {
            cout << "ya_win!\n";
            cout << nowb - nowa << '\n';
        }
    }
    return 0;
}

小沙の串串

因为要求字典序最大,所以当k足够大的时候直接输出从大到小,但如果k不够大的时候,需要贪心的把较大的放在前面,因为只需要输出字典序最大的,所以放到最后的字符的顺序我们是可以决定的,这种情况下可能会有未能遍历完全部n个字符,说明最后面的未修改的地方就是字典序最大的情况,所以直接把三部分拼在一起即可,如果全部遍历到了,还有k次操作没有用,需要考虑的是前面放在最后的不一定比s1中遍历过的大,所以要选择s1中后k个再放到最后,这样才能保证字典序是最大的,反例6,3,ebdeec,正确答案eeedcb,错误答案eeecdb

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    string s;
    cin >> s;
    queue<int> q[26];
    for (int i = 0; i < n; i++) {
        q[s[i] - 'a'].push(i);
    }
    string s1 = "", s2 = "" , s3 = "";
    int now = 0;
    while (k) {
        for (int i = 25; i >= 0; i--) {
            while (!q[i].empty() && q[i].front() < now) {
                q[i].pop();
            }
            if (!q[i].empty() && q[i].front() <= now + k) {
                int nxt = q[i].front();
                q[i].pop();
                k -= nxt - now;
                while (now < nxt) {
                    s2 += s[now];
                    now++;
                }
                s1 += s[now];
                now++;
                break;
            }
        }
        if (now == n) {
            break;
        }
    }
    for (int i = now; i < n; i++) {
        s3 += s[i];
    }
    while (k && !s1.empty() && s3.size() == 0) {
        s2 += s1[s1.size() - 1];
        s1.pop_back();
        k--;
    }
    sort(s2.begin(), s2.end());
    reverse(s2.begin(), s2.end());
    cout << s1 + s3 + s2 << '\n';
    return 0;
}

小沙の店铺

按题意模拟即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    LL x, y, k, n, T;
    cin >> x >> y >> k >> n >> T;
    LL ans = -1;
    LL m = n, sum = 0, sum1 = 0;
    for (int i = 1; i <= m; i++) {
        sum += n * x;
        sum1 += n;
        if (sum >= T) {
            ans = i;
            break;
        }
        if (sum1 >= k) {
            x += y * (sum1 / k);
            sum1 = sum1 % k;
        }
        n--;
    }
    cout << ans << '\n';
    return 0;
}

小沙の金银阁

考虑起码不亏本的情况下的方案,不难发现是1 1 2 4 8 16...,即当前数必须是前面所有数的和才能保证在每一轮结束都起码不会亏本,再计算该序列的前缀和,发现当n>51的时候前缀和大于1e15,所以n最多只能到51,不亏本的方案是最起码的,所以m必须大于b[n],即最起码要这么压石头才能保证不亏本,否则没有符合方案,然后遍历1~n,计算m/b[n-i+1]的值,m/b[n-i+1]就是从第i位开始还能再放多少1 1 2 4 8 16...这个序列,因为从第1位到第i-1位已经影响不到后面的计算了并且也是贪心地放置了符合条件的、获取灵石最多的方案,最优方案相当于字典序最大,所以前面的数要尽可能的大

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    vector<int> a(52), b(52);
    a[1] = 1;
    a[2] = 1;
    b[1] = 1;
    b[2] = 2;
    for (int i = 3; i < 52; i++) {
        a[i] = 2 * a[i - 1];
        b[i] = b[i - 1] + a[i];
    }
    int n, m;
    cin >> n >> m;
    if (n > 51) {
        cout << "-1\n";
        return 0;
    }
    if (m < b[n]) {
        cout << "-1\n";
        return 0;
    }
    vector<int> ans(n + 1);
    for (int i = 1; i <= n; i++) {
        ans[i] = a[i];
        m -= ans[i];
    }
    for (int i = 1; i <= n; i++) {
        int p = m / b[n - i + 1];
        for (int j = i, k = 1; j <= n; j++, k++) {
            ans[j] += a[k] * p;
            m -= a[k] * p;
        }
        if (m == 0) {
            break;
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << ans[i] << " \n"[i == n];
    }
    return 0;
}

小沙の抱团 easy

一次想要淘汰最多的人,可知剩下的人数就是当前数的一半+1个

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    LL n;
    cin >> n;
    LL ans = 0;
    while (n > 2) {
        LL mid = n / 2 + 1;
        n = mid;
        ans++;
    }
    cout << ans << '\n';
    return 0;
}

小沙の抱团 hard

DP,因为每个命令可以无限次使用,相当于完全背包,dp[i]就是把n变成i所需要的最小代价,初始状态dp[n]=0,找到从1开始最小能dp到的数即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
struct node {
    int b, x;
};
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<node> a(m);
    for (int i = 0; i < m; i++) {
        cin >> a[i].b >> a[i].x;
    }
    vector<int> dp(n + 1, 1e18);
    dp[n] = 0;
    for (int i = n; i >= 1; i--) {
        for (int j = 0; j < m; j++) {
            if (i > a[j].x && i % a[j].x) {
                dp[i - i % a[j].x] = min(dp[i - i % a[j].x], dp[i] + a[j].b);
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        if (dp[i] != 1e18) {
            cout << dp[i] << '\n';
            break;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值