# 牛客周赛87题解(出题人)

A. 小苯的V图

直接按题意判断即可。

void solve() {
    int a, b, c;
    cin >> a >> b >> c;
    if(a > b && b < c) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
}

B. 小苯的数字切割

做法1:枚举切割的位置,统计所有情况取max。

做法2:实际上我们会发现,最优解一定仅存在于切 或者 ,因此我们两种情况取max即可

void solve() {
    string s;
    cin >> s;
    assert(s.size() > 1);
    for(auto & c : s) {
        assert(c != '0');
    }
    int n = stoll(s);
    n /= 10;
    n += (s.back() - '0');
 
    int m = 0;
    for(int i = 1; i < s.size(); i++) {
        m = (m * 10) + (s[i] - '0');
    }
    m += (s[0] - '0');
    cout << max(n, m) << endl;
}

C. 小苯的Z串匹配

我们会发现,<和> 固定的位置是不能改变的,这启发我们贪心地先模拟将所有 '<' 和 '>' 对应的 都改成对应正负性的数字(可以直接取 )。

最后我们再从前往后枚举匹配 'Z'。如果枚举到的地方不匹配,不难发现由于我们不好改变已经确定的 的部分,因此我们直接改 ,把它的正负性改为和 一样即可。(这一步直接赋值就最方便。)

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    string s;
    cin >> s;
    s = " " + s;
 
    int ans = 0;
    for(int i = 1; i <= n; i++) {
        if(s[i] == '<') {
            if(a[i] >= 0) {
                a[i] = -1;
                ans++;
            }
        } else if(s[i] == '>') {
            if(a[i] <= 0) {
                a[i] = 1;
                ans++;
            }
        }
    }
    assert(s[1] != 'Z');
    for(int i = 2; i <= n; i++) {
        if(s[i] == 'Z') {
            if(a[i] * a[i - 1] <= 0) {
                ans++;
                assert(a[i - 1] != 0);
                a[i] = a[i - 1]; // 直接继承正负性
            }
        }
    }
    cout << ans << endl;
}

D. 小苯的最大和

本题实际上带一些诈骗色彩,想启发同学们去写贪心,因为根据 ”麦乐鸡定理“,所有长度大于 的连续负数段都可以全部删干净,因此最后所有负数段长度都为 ,这时可能会有同学去思考一些比较困难的贪心。

然而以上想法是不正确的,本题正解为 。 我们考虑,其实题目中删除一段区间再拼接起来剩下的,再在拼接后的数组上删除,其实等价于在原数组中选择若干个不交的区间 (每个区间的长度都是 )并删除。

比如 12345678 先删除 34 变成 125678,再删除 256 变成 178。 实际上等价于选择了 [2,3] 和 [4,5,6] 这两段长度分别为 的区间。

因此转化后,我们的问题变成了非常经典的 ,我们考虑如下

表示前 个数字中,用以上方法删除区间得到的最大和,则决策有三种: 不删除数字,则 删长度为 的区间,则: 删长度为 的区间,则:

注意后两种转移有 的限制。

void solve() {
    int n;
    cin >> n;
    vector<int> dp(n + 1, -1e18);
    dp[0] = 0;
    for(int i = 1, x; i <= n; i++) {
        cin >> x;
        dp[i] = dp[i - 1] + x;;
        if(i >= 2) {
            dp[i] = max(dp[i], dp[i - 2]);
        }
        if(i >= 3) {
            dp[i] = max(dp[i], dp[i - 3]);
        }
    }
    cout << dp[n] << endl;
}

E. 小苯的数组构造

位运算题我们直接考虑拆位,由于题目限制了需要全正,因此贪心地,我们尽量能给所有数字填就给所有数字填。

考虑一个位上 对应的四种情况:

,这种显然不能填东西,直接跳过。

,碰到这种显然直接无解,因为 当前是 意味着必须所有 都是 ,则 就不可能产生

,这种我们分 的奇偶性看,如果 是偶数,则我们可以给所有 当前位填 ,否则需要跳过一个。

,和 的情况完全对称,只不过反过来变成 是偶数需要跳过。

按上述最贪心的构造方法如果还构造不出答案,则一定无解。

void solve() {
    int n, x, y;
    cin >> n >> x >> y;
    if((x & y) != y) {
        cout << "NO" << endl;
        return ;
    }
    int skip = 1;
    vector<int> ans(n + 1);
    for(int j = 31; j >= 0; j--) {
        int a = (x >> j & 1), b = (y >> j & 1);
        if(a == 0) continue; // 0 0 不用考虑
        int now = (a ^ b) ^ (n & 1);
        if(now == 0) {
            for(int i = 1; i <= n; i++) {
                if(i == skip) continue;
                ans[i] |= (1LL << j);
            }
            skip = min(skip + 1, n);
        }
        else {
            for(int i = 1; i <= n; i++) { // 无脑全塞 1
                ans[i] |= (1LL << j);
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        if(ans[i] <= 0) {
            cout << "NO" << endl;
            return ;
        }
    }
     
    int v1 = 0, v2 = 0;
    for(int i = 1; i <= n; i++) {
        v1 |= ans[i];
        v2 ^= ans[i];
    }
     
    if(v1 != x || v2 != y) {
        cout << "NO" << endl;
    } else {
        cout << "YES" << endl;
        for(int i = 1; i <= n; i++) {
            cout << ans[i] << " \n"[i == n];
        }
    }
}

F. 小苯的ovo2.0

一句话题解: 最优解一定是,所有被改成 的问号处于一个连续区间内,区间中不存在改为 的问号。

因此我们直接 枚举所有可能的区间,将区间内的问号改成 ,区间外的全改 ,再跑一个 计数即可求出答案,对所有枚举的答案取 即可。

严格证明比较复杂,大家可以感性地理解成:所有的 都会产生:其左侧 个数 其右侧 个数。的贡献,而 个数固定的情况下,这个贡献可以写成一个凸的二次函数。而对于所有的 产生的贡献之和(也就是答案)实际上就是多个二次函数的加和,其结果依然是凸的二次函数,因此最优的改变方案里不会出现: 子序列这种结构。因此只能是所有 都聚在一堆。

void solve() {
    string s;
    cin >> s;
    int n = s.size();
     
    auto calc = [&](int l, int r) -> int {
        string S = s;
        for(int i = 0; i < n; i++) {
            if(S[i] != '?') continue;
            if(i < l || i > r) {
                S[i] = 'o';
            } else {
                S[i] = 'v';
            }
        }
         
        int o = 0, ov = 0, ovo = 0;
        for(auto & c : S) {
            if(c == 'o') {
                o++;
                ovo += ov;
            } else {
                ov += o;
            }
        }
        return ovo;
    };
     
    int ans = 0;
    for(int l = 0; l < n; l++) {
        for(int r = l - 1; r < n; r++) {
            int now = calc(l, r);
            ans = max(ans, now);
        }
    }
    cout << ans << endl;
}

Thanks.

#牛客周赛#
全部评论
小苯坏事做尽
14 回复 分享
发布于 03-31 10:00 云南
你好,想问一下为什么这里最后还要判断一下是否构造的符合条件呀。我们构造的时候不就是按着这个结果来进行构造的吗
4 回复 分享
发布于 03-31 09:03 重庆
小苯坏事做尽
2 回复 分享
发布于 03-31 10:25 四川
C题把“正负性”打成“奇偶性”了吧? 另外,其实遇到s[i] ==‘Z’ 只需要s[i] =s[i-1] 就行了 然后判断a[i] 满不满足条件,只需要遍历一次
1 回复 分享
发布于 03-31 19:41 四川
C题给 a[i] 赋值的地方改为 a[i] *= -1; 为什么会出错?
点赞 回复 分享
发布于 04-07 21:01 江西
D为什么不可以用贪心啊?
点赞 回复 分享
发布于 04-02 19:21 河南
内层循环为什么要从l-1枚举?为什么不是l+1开始!
点赞 回复 分享
发布于 04-01 08:19 湖北
最后的F题,是不是让r=l,要不然当l=0,r=-1会出现越界把
点赞 回复 分享
发布于 03-31 10:19 河南

相关推荐

头像
05-16 11:16
已编辑
东华理工大学 Java
牛客737698141号:盲猜几十人小公司,庙小妖风大,咋不叫她去4️⃣呢😁
点赞 评论 收藏
分享
评论
17
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务