10.10 携程笔试

前两题都比较简单,这里给出后两题我的解法

  1. 给一个长为n的数组a,将其分成恰好m个非空子数组,对每个子数组内部求gcd,求m个gcd值的和的最大值,1≤m≤n≤400。

    典型的区间dp问题。

    首先计算gcd[l][r]表示数组下标l到r的gcd值,后续考虑dp[i+1][j+1]表示到第i个数字,恰好分成j个子数组的gcd的和的最大值。初始dp[0][0]=0,其余为-inf。转移时遍历当前子数组的左端点[0,i],即:

    时间复杂度

  2. 给一个长为n的数组a,有q次查询,每次查询为[op, l, r],若op=1则计算 a[l] & a[l+1] | a[l+2] & a[l+3] | a[l+4] ... a[r], 否则计算 a[l] | a[l+1] & a[l+2] | a[l+3] & a[l+4] ... a[r]。1≤n,q≤1e5

    这里我给出我的解法,比较复杂,不知道有没有更好的方法。

    首先观察两种查询,可以发现第二种查询可以转换成第一种查询,这是因为

    修改成 即可,查询后再修改回来。因此只需要处理第一种查询即可。

    涉及到区间查询和单点修改,考虑使用线段树。代码:

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

const int M = (1 << 30) - 1;

struct node {
    int v1 = 0, v2 = M;
    int v3 = 0, v4 = M;
};

node merge(node& a, node& b) {
    node res;
    res.v1 = (a.v1 & b.v2) | b.v1;
    res.v2 = a.v2 & b.v2;
    res.v3 = (a.v3 & b.v4) | b.v3;
    res.v4 = a.v4 & b.v4;
    return res;
}

struct segtree {
    vector<node> tr;
    int n;

    void build(vector<int>& a, int p, int l, int r) {
        if (l == r) {
            if (l & 1) tr[p].v1 = tr[p].v4 = a[l];
            else tr[p].v3 = tr[p].v2 = a[l];
            return;
        }
        int mid = (l + r) / 2;
        build(a, p<<1, l, mid);
        build(a, p<<1|1, mid+1, r);
        tr[p] = merge(tr[p<<1], tr[p<<1|1]);
    }

    segtree(vector<int> &a) : n(a.size()) {
        tr.resize(n * 4 + 5);
        build(a, 1, 0, n-1);
    }

    node query(int p, int l, int r, int s, int t) {
        if (s > r || t < l) return node{};
        if (s <= l && t >= r) return tr[p];
        int mid = (l + r) / 2;
        node p1, p2;
        if (mid >= s) p1 = query(p<<1, l, mid, s, t);
        if (mid < t) p2 = query(p<<1|1, mid+1, r, s, t);
        return merge(p1, p2);
    }

    void update(int p, int l, int r, int x, int v) {
        if (l == r) {
            if (l & 1) tr[p].v1 = tr[p].v4 = v;
            else tr[p].v3 = tr[p].v2 = v;
            return;
        }
        int mid = (l + r) / 2;
        if (mid >= x) update(p<<1, l, mid, x, v);
        else update(p<<1|1, mid+1, r, x, v);
        tr[p] = merge(tr[p<<1], tr[p<<1|1]);
    }
};

int main() {
    int n, q;
    cin >> n >> q;
    vector<int> a(n+1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    segtree tree(a);
    while (q--) {
        int op, l, r;
        cin >> op >> l >> r;
        if (op == 1) {
            node res = tree.query(1, 0, n, l, r);
            if (l & 1) cout << res.v1 << '\n';
            else cout << res.v3 << '\n';
        } else {
            tree.update(1, 0, n, l-1, M);
            node res = tree.query(1, 0, n, l-1, r);
            if (l-1 & 1) cout << res.v1 << '\n';
            else cout << res.v3 << '\n';
            tree.update(1, 0, n, l-1, a[l-1]);
        }
    }
}
#软件开发笔面经##携程#
全部评论
4.位运算+贪心。首先位运算变成30个01序列。贪心:&1不会变,|0不会变,只需要找&0和|1就行,找最后的 &0和|1,比较位置。若无 &0和|1,答案加上l所在的位。
1 回复 分享
发布于 10-10 21:56 上海
不会线段树,想着交个分组排序骗点分还挂0了
点赞 回复 分享
发布于 10-10 16:11 陕西
你有收到面试吗
点赞 回复 分享
发布于 10-15 17:43 上海

相关推荐

4 6 评论
分享
牛客网
牛客企业服务