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

阿宁的签到题

模拟

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int x;
    cin >> x;
    if (x >= 1 && x <= 7) {
        cout << "very easy\n";
    } else if (x <= 233) {
        cout << "easy\n";
    } else if (x <= 10032) {
        cout << "medium\n";
    } else if (x <= 114514) {
        cout << "hard\n";
    } else if (x <= 1919810) {
        cout << "very hard\n";
    } else {
        cout << "can not imagine\n";
    }
    return 0;
}

阿宁的倍数

首先预处理出每个数都是谁的倍数,时间复杂度nlogn,然后从1~n,对所有a[i]整除的数+1,相当于前缀和,最后查询的时候只需要算出 能被整除的-x及以前能被整除的 即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
vector<int> G[200010];
int sum[200010], ans[400010];
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    for (int i = 1; i <= 200000; i++) {
        for (int j = i; j <= 200000; j += i) {
            G[j].push_back(i);
        }
    }
    int n, q;
    cin >> n >> q;
    vector<int> a;
    a.push_back(0x3f3f3f3f);
    for (int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        a.push_back(x);
        for (auto it : G[x]) {
            sum[it]++;
        }
        ans[i] = sum[x];
    }
    while (q--) {
        int op, x;
        cin >> op >> x;
        if (op == 1) {
            n++;
            a.push_back(x);
            for (auto it : G[x]) {
                sum[it]++;
            }
            ans[n] = sum[x];
        } else {
            cout << sum[a[x]] - ans[x] << '\n';
        }
    }
    return 0;
}

阿宁的大背包

计算每个数对答案的贡献,可知是个杨辉三角,即越是中间的数贡献越大,所以把大的数优先放在中间,小的数放两边,暴力计算即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
const int mod = 1e9 + 7;
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    deque<int> q;
    for (int i = n; i >= 1; i--) {
        if (i % 2 == 0) {
            q.push_front(i);
        } else {
            q.push_back(i);
        }
    }
    vector<int> a, ans;
    while (!q.empty()) {
        int x = q.front();
        q.pop_front();
        a.push_back(x);
    }
    ans = a;
    for (int i = 0; i < n - 1; i++) {
        vector<int> b;
        int len = a.size();
        for (int j = 0; j < len - 1; j++) {
            b.push_back((a[j] + a[j + 1]) % mod);
        }
        a = b;
    }
    cout << a[0] << '\n';
    for (int i = 0; i < n; i++) {
        cout << ans[i] << " \n"[i == n - 1];
    }
    return 0;
}

阿宁的毒瘤题

首先容易想到的是如果要删掉u的话一定是从第一个u或者最后一个u当中选择一个更好的删除,因为当这个u后面出现一个d的时候,d后面再遇到u的话每个u的贡献就多了1,因为可以选择多个d组成udu,如果选两个d中间的u,u左边的贡献从1开始,右边也是从1开始,明显小于两端的u,因此能够求出两端的一个u分别能产生多少贡献,然后如果要删掉d的话,需要计算一个d能影响多少,即d左边u的个数*d右边u的个数,然后找出三者贡献最大的,修改掉即可

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 s = "";
    cin >> s;
    int len = s.size();
    int sum = 0;
    vector<int> x, y;
    for (int i = 0; i < len; i++) {
        if (s[i] == 'u') {
            sum++;
        }
        if (s[i] == 'd') {
            x.push_back(sum);
        }
    }
    sum = 0;
    for (int i = len - 1; i >= 0; i--) {
        if (s[i] == 'u') {
            sum++;
        }
        if (s[i] == 'd') {
            y.push_back(sum);
        }
    }
    int pre = 0, end = len - 1, ans1 = 0, ans2 = 0, cnt = 0;
    while (s[pre] != 'u' && pre < len) {
        pre++;
    }
    while (s[end] != 'u' && end >= 0) {
        end--;
    }
    for (int i = pre; i < len; i++) {
        if (s[i] == 'd') {
            cnt++;
        }
        if (s[i] == 'u') {
            ans1 += cnt;
        }
    }
    cnt = 0;
    for (int i = end; i >= 0; i--) {
        if (s[i] == 'd') {
            cnt++;
        }
        if (s[i] == 'u') {
            ans2 += cnt;
        }
    }
    int l = 0, r = x.size() - 1;
    int idx = 0, ans3 = 0;
    for (int i = 0; i < len; i++) {
        if (s[i] == 'd') {
            int res = x[l] * y[r];
            if (res > ans3) {
                idx = i;
                ans3 = res;
            }
            l++;
            r--;
        }
    }
    if (ans1 >= ans3 && ans1 >= ans2) {
        s[pre] = 'z';
        cout << s << '\n';
    } else if (ans2 >= ans1 && ans2 >= ans3) {
        s[end] = 'z';
        cout << s << '\n';
    } else {
        s[idx] = 'z';
        cout << s << '\n';
    }
    return 0;
}

阿宁的生成树

不难想到的是,1和所有数互质,所以如果是gcd(i,j),那么一定和1连接最优,边权为1,可知对于n个数素数的密度大概是n/ln(n),所以当只能和1连lcm(i,j)时,考虑其和[i+k+1,n]之间的连gcd(i,j)边,因为素数密度不大,所以暴力找到一个gcd(i,j)==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);
    int n, k;
    cin >> n >> k;
    function<LL(LL, LL)> gcd = [&](LL a, LL b) {
        return b == 0 ? a : gcd(b, a % b);
    };
    int ans = 0;
    for (int i = 2; i <= n; i++) {
        if (i - 1 <= k) {
            int p = i;
            for (int j = i + k + 1; j <= n; j++) {
                p = min(p, gcd(i, j));
                if (p == 1) {
                    break;
                }
            }
            ans += p;
        } else {
            ans++;
        }
    }
    cout << ans << '\n';
    return 0;
}

阿宁的二进制

根据题意可知,经过有限次且次数并不大的操作即可把一个数变成1,所以每次选最大的数进行操作,提前记录每次操作后剩下的最大值是多少即可

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define int long long
int ans[1000010];
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, q;
    cin >> n >> q;
    priority_queue<int, vector<int>, less<int>> qb, qc;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        qb.push(a[i]);
    }
    int sum = 0;
    qc = qb;
    ans[sum] = qc.top();
    while (qc.top() != 1) {
        int now = qc.top();
        qc.pop();
        now = __builtin_popcount(now);
        qc.push(now);
        sum++;
        ans[sum] = qc.top();
    }
    while (q--) {
        int k;
        cin >> k;
        if (k >= sum) {
            cout << "1\n";
            continue;
        }
        cout << ans[k] << '\n';
    }
    return 0;
}

阿宁的整数配对

为了尽可能地大,只能小的负数与小的负数相乘,大的正数与大的正数相乘,得到的结果相加才越大,因此每次选择小的负数与小的负数相乘和大的正数与大的正数相乘中最大的,如果正数小于2或负数小于2,能乘0优先乘0,否则一定会出现负数

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, k;
    cin >> n >> k;
    LL ans = 0, cnt = 0;
    priority_queue<int, vector<int>, less<int>> qa;
    priority_queue<int, vector<int>, greater<int>> qb;
    for (int i = 0; i < n; i++) {
        int p;
        cin >> p;
        if (p > 0) {
            qa.push(p);
        } else if (p == 0) {
            cnt++;
        } else {
            qb.push(p);
        }
    }
    while (k) {
        int len1 = qa.size(), len2 = qb.size();
        int x, y, xx, yy;
        if (len1 >= 2 && len2 >= 2) {
            x = qa.top();
            qa.pop();
            y = qa.top();
            qa.pop();
            xx = qb.top();
            qb.pop();
            yy = qb.top();
            qb.pop();
            if (x * y > xx * yy) {
                ans += x * y;
                qb.push(xx);
                qb.push(yy);
            } else {
                ans += xx * yy;
                qa.push(x);
                qa.push(y);
            }
        } else if (len1 >= 2) {
            x = qa.top();
            qa.pop();
            y = qa.top();
            qa.pop();
            ans += x * y;
        } else if (len2 >= 2) {
            xx = qb.top();
            qb.pop();
            yy = qb.top();
            qb.pop();
            ans += xx * yy;
        } else if (cnt > 0) {
            if (len1 > 0) {
                qa.pop();
                cnt--;
            } else if (len2 > 0) {
                qb.pop();
                cnt--;
            } else {
                cnt -= 2;
            }
        } else {
            x = qa.top();
            qa.pop();
            xx = qb.top();
            qb.pop();
            ans += x * xx;
        }
        k--;
    }
    cout << ans << '\n';
    return 0;
}

阿宁讨伐虚空

找出脆皮还有的区间除以总的区间长度即可

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 x, l, r;
    cin >> x >> l >> r;
    if (x > r) {
        cout << "1.000000000\n";
    } else if (x < l) {
        cout << "0.000000000\n";
    } else {
        int t = r - x + 1, q = x - l;
        int sum = r - l + 1;
        double ans = 1.0 * q / sum;
        cout << fixed << setprecision(10) << ans << '\n';
    }
    return 0;
}

阿宁前往沙城

可知,如果前往沙城最近的路的长度等于所有的边数,那么第一条路必须走边权的长度,其他路边权全部变成1,也就是走过的路全部毁灭,下一条路边权变成1,如果前往沙城最近的路的长度小于所有的边数,那么前往沙城所有路的边权都是1,选择其他不是前往沙城的路毁灭,要走的路边权变成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);
    int n, m;
    cin >> n >> m;
    vector<vector<pair<int, int>>> G(n + 1);
    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        G[u].push_back(make_pair(v, w));
        G[v].push_back(make_pair(u, w));
    }
    vector<bool> vis(n + 1);
    vector<int> dep(n + 1);
    queue<int> q;
    q.push(1);
    vis[1] = true;
    while (!q.empty()) {
        int now = q.front();
        q.pop();
        for (auto v : G[now]) {
            if (!vis[v.first]) {
                dep[v.first] = dep[now] + 1;
                q.push(v.first);
                vis[v.first] = true;
            }
        }
    }
    if (m == dep[n]) {
        cout << dep[n] - 1 + G[1][0].second << '\n';
    } else {
        cout << dep[n] << '\n';
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值