百度后端笔试 2023-04-10

T1 小红的子数组拆分

题意

小红拿到了一个长度为n的数组,她希望把该数组拆分成k个非空子序列(每个元素必须出现在某个子序列中,且恰好出现一次),使得这k个子序列的平均数之和尽可能小。你能帮帮她吗?

注,子序列可以不连续。例如数组为[3,2,1,3],k=2时,子序列可以拆分为[3,1]和[2,31]。

1 <= k, n <= 1e5, -10^9 <= ai <= 10^9

思路

对于一个数字 x,假设分配到了长度为 y 的数组中,那么它对最终答案的贡献是 x / y。

对 x 分正负讨论:

  • 正数,使它尽可能的分配到更长的子数组中。
  • 负数,使它尽可能的分配到长度为 1 的子数组中。

也就是说数字越小,分配到的子数组长度应该尽可能的小,于是有了以下做法:

对数组排序,然后前 k - 1 个数字单独分组,最后 n - k + 1 个 数字分成一组。

时间复杂度 O(nlogn)。

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    int n, k;
    cin >> n >> k;
    vector<ll> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    sort(a.begin(), a.end());
    ll res = 0;
    for (int i = 0; i < k - 1; i++) {
        res += a[i];
    }
    ll sum = 0;
    for (int i = k - 1; i < n; i++) {
        sum += a[i];
    }
    double ans = res + 1.0 * sum / (n - k + 1);
    printf("%.10f\n", ans);
}

T2 red 回文串

题意

给定一个整数x,请你构造一个仅由'r'、‘e'、’d’三种字符组成的字符串,其中回文子串的数量恰好为x。字符串的长度不得超过10^5。

1 <= x <= 10^9

思路

这个题目老朋友了,由相同字符构成的长度为 n 的字符串共有 (1 + n) * n / 2 个回文串,我们先找到最大的 n 使得 (1 + n) * n / 2 <= x,用 n 个 r 去填充,然后剩下按照 "red" 去补齐即可。

时间复杂度 O(sqrt(n))。

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ll x;
    cin >> x;
    ll n = 1, sum = 0;
    string s = "";
    while (sum + n <= x) {
        sum += n;
        s += "r";
        n++;
    }
    int c = 0;
    for (int i = sum + 1; i <= x; i++) {
        if (c == 0) {
            s += 'e';
        } else if (c == 1) {
            s += 'd';
        } else {
            s += 'r';
        }
        c = (c + 1) % 3;
    }
    cout << s << endl;
    return 0;
}

T3 小红的子树操作

题意

小红拿到了一棵有根树,i 号节点的权值为 ai 。已知1号节点为根节点。

小红有q次操作,每次操作会选择一个节点x,使得x为根的子树上,所有节点的权值乘以y。

小红想知道,在q次操作结束以后,对于节点 i,以节点 i 为根的子树的所有点权值乘积末尾有多少个0?

1 <= n, q <= 10^5

1 <= ai, y <= 10^9

思路

这个题目也是老朋友了。

首先,乘积末尾 0 的个数,只跟乘数分解质因数后 2 和 5 的指数有关,这两个指数的最小值即为末尾 0 的个数。

然后,对以 x 为根的子树操作,只需要先给 x 打上标记,q 次操作全部打上标记后,dfs 的过程中将标记传递到子树上的每个结点,累计求和。

最后回溯的时候,再求一次和,表示子树所有数的乘积。

时间复杂度 O(n+qlogy)。

// 小红的子树操作
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    int n, q;
    cin >> n;
    vector<ll> a(n), c2(n), c5(n);
    vector<vector<int>> g(n);

    // 计算 x 分解质因数后 base 的指数
    auto get = [&](ll x, int base) {
        ll cnt = 0;
        while (x % base == 0) {
            cnt++;
            x /= base;
        }
        return cnt;
    };

    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 0; i < n - 1; i++) {
        int x, y; cin >> x >> y;
        x--; y--;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    cin >> q;
    for (int i = 0; i < q; i++) {
        int x, y;
        cin >> x >> y;
        c2[x - 1] += get(y, 2);
        c5[x - 1] += get(y, 5);
    }
    
    function<void(int, int)> dfs = [&](int x, int f) {
        // 递归过程中,给 x 的子节点 y 累加标记
        for (auto &y : g[x]) {
            if (y == f) continue;
            c2[y] += c2[x];
            c5[y] += c5[x];
            dfs(y, x);
        }

        // 回溯过程中,求以 x 为根的子树中所有节点的标记之和。
        for (auto &y : g[x]) {
            if (y == f) continue;
            c2[x] += c2[y];
            c5[x] += c5[y];
        }
        
        // 这里对于 x 原本的权值要单独算,这一部分不能递归下去
        c2[x] += get(a[x], 2);
        c5[x] += get(a[x], 5);
    };

    dfs(0, -1);
    for (int i = 0; i < n; i++) {
        cout << min(c2[i], c5[i]) << " \n"[i == n - 1];
    }
    return 0;
}
#软件开发2023笔面经##百度##笔试#
今夕的求职日记 文章被收录于专栏

记录2023年-2024年的笔试、面试问题~

全部评论
怪了,第一题我用java,也是这思路,只能过30%,用printf了
点赞 回复 分享
发布于 2023-04-10 21:23 陕西
点赞 回复 分享
发布于 2023-04-10 22:19 江苏

相关推荐

5 17 评论
分享
牛客网
牛客企业服务