• VP 9.13 Codeforces Round #687 (Div. 1, based on Technocup 2021 Elimination Round 2)


    自闭了,这场 B 直接调了大半年,最后 Wrong Answer on test 51

    CF1456A. Bouncing Ball

    前缀和一下,然后直接 calc 就行了。

    CF1456B. XOR-gun

    考场上一直调 trie 树做法,然后发现是萎的。

    实际上不要那么麻烦,其实有一个性质:

    (n > 2 imes log max{a_i})(n > 60) 时,答案一定为 1 。

    考虑证明 :

    (b_i) 表示 (a_i) 二进制下的最高位, 那么有 (b_1 le b_2 le b_3 le dots le b_n) , 如果 (exists i, b_{i - 1} = b_{i} = b_{i + 1}) , 那么 我们只需要对 (a_i , a_{i + 1}) 进行一次操作就满足要求, 那么当 (n > 2 imes log max{a_i}) 时,一定满足要求。

    因此我们只要暴力 check 就行了。

    CF1456C. New Game Plus!

    因为 B 题, 直接没有开这道题, 看了题目就跑路了。

    重置 (k) 次等价于我们要将所有 boss 分成 (k + 1) 组, 然后就可以拿一个堆来维护,首先我们按照 a 值从大到小来打 boss ,那么每次找到最大的一组然后更新,可以发现是最优的。

    CF1456D. Cakes for Clones

    这个状态设计好神啊

    显然是 DP,状态如下 :

    mintime[i] 当前在 i 点,i 前面的点(不含 i )都被接到了的最短时间

    dp[i][j] (j > i) 当前在 i , 分身在 j 是否可行。

    可见,mintime[i] 的情况下,可能没有派出分身或者分身在 ℹ 前面的位置, dp[i][j] 的情况下,分身在 ℹ 后面的位置。(这里的位置在后面是指蛋糕出现时间比 ℹ 晚的位置, 前面同理)

    那么对于情况一 :

    1. 直接走到 i + 1 。先在 i 处放置一个分身,然后走到 i + 1 处,也就是 (mintime[i] + dis(i,i + 1)) ,贡献到 (mintime[i + 1])
    2. 防止别的分身为后来所用,枚举 (j > i + 1) ,那么先放一个分身到 (i) ,然后走到 (j) ,等到 (i) 处蛋糕落下,分身接住蛋糕,然后在 (j) 处放分身,接着走到 (i + 1) , 也就是 (max(mintime[i] + dis(i, j),t[i]) + dis(j,i + 1)) ,如果可行,则 dp[i + 1][j] = 1

    对于情况二 :

    枚举 (j ge i + 1) , 有 :

    1. (j e i + 1) ,只能从 dp[i][j] 转移到 dp[i + 1][j]

    2. (j = i + 1) , 又有

      直接走到 (i + 2) , (mintime[i] + dis(i, i + 2)) 转移到 (mintime[i + 2])

      再枚举 (k > i + 2) , 表示先走到 (k) , 然后让分身借住蛋糕,再走会 (i + 2)

    代码 :

    // mt = mintime
    #include <bits/stdc++.h>
    #define in read()
    #define rep(i, x, y) for(int i = (x); i <= (y); i++)
    #define per(i, x, y) for(int i = (x); i >= (y); i--)
    
    using namespace std;
    
    const int N = 5010;
    const int inf = 0x3f3f3f3f;
    
    bool dp[N][N];
    int a[N], n;
    ll t[N], mt[N];
    
    inline int dis(int x, int y) { return abs(a[x] - a[y]); }
    
    int main() {
        n = in; rep(i, 1, n) t[i] = in, a[i] = in;
        memset(mt, 0x3f, sizeof mt);
        mt[0] = 0; t[0] = 0; a[0] = 0;
        rep(i, 0, n - 1) {
            if(mt[i] > t[i]) continue;
            mt[i + 1] = min(mt[i + 1], max(mt[i] + dis(i, i + 1), t[i]));
            rep(j, i + 2, n) if(max(mt[i] + dis(i, j), t[i]) + dis(j, i + 1) <= t[i + 1]) dp[i + 1][j] = true;
            if(dis(i, i + 1) + t[i] <= t[i + 1]) rep(j, i + 2, n) dp[i + 1][j] |= dp[i][j];
            if(dp[i][i + 1]) {
                mt[i + 2] = min(mt[i + 2], max(t[i + 1], t[i] + dis(i, i + 2)));
                rep(j, i + 3, n)
                    if(max(t[i] + dis(i, j), t[i + 1]) + dis(j, i + 2) <= t[i + 2]) dp[i + 2][j] = true;
            }
        }
        if(mt[n] <= t[n] || dp[n - 1][n]) cout << "YES" << endl;
        else cout << "NO" << endl; return 0;
    }
    

    CF1456E. XOR-ranges

    考虑数位 DP ,数位 DP 最难的就是消除上下界限制,因为是二进制,那么对于一个位置 (i) , (L_i, R_i) 的二进制下一定有一段 lcp , 然后考虑下一位一定是 (L_i) 为 0 , (R_i) 为 1 ,此时一定有一个限制解除了。

    因此,一个数一定可以表示为前面有一段和 (L_i)(R_i) 相同,然后下一位不同,接下来乱填都是满足限制的,我们称该数与 (L_i)(R_i) 开始不同然后之后乱填的位置为开始脱离限制的位置。

    我们一个一个二进制位来考虑(从右到左,如最后一位成为第一位),考虑每个数开始脱离限制的二进制位,那么整个序列一定被分成了若干段,每一段 ([l,r])有一个最大的脱离限制的位置,后面的位置就是可以随便乱填的,这个最小贡献是可以通过左右两边 (l - 1, r + 1) 的情况来确定的,具体见代码。

    我们设 dp[bit][l][r][0/1][0/1][0/1][0/1] 表示我们目前的区间是 ([l,r]) , 从小到大考虑到了第 (bit) 位,[0 / 1][0/ 1] 表示的是 (l - 1) 紧贴的限制是 (L[l - 1]) 还是 (R[l - 1]) , 以及 (l - 1) 上的数是否在第 (bit) 位不同,(r + 1) 同理 下 ans 的最小值。

    考虑转移,设当前的 DP 值状态为 res = dp[bit][l][r][l1][l2][r1][r2]

    1. (bit) 位没有脱离限制的,那么 (res leftarrow dp[bit + 1][l][r][l1][0][r1][0])
    2. (bit) 位有脱离限制的,枚举 (k) ,表示 (a_k) 在第 (bit) 位脱离了限制(注意还要check 是否之后能够随便乱填),那么有 (res leftarrow dp[bit][l][k - 1][l1][l2][t][1] + dp[bit][k + 1][r][t][1][r1][r2])

    还要注意以下自始自终都等于 (L_i) 或者 (R_i) 的,具体见代码。

    最后终止状态:

    (bit = K) 时,必须要 (l > r) 才是合法的,为 (0) , 因为每个数必需要脱离限制。

    代码 :

    #include <bits/stdc++.h>
    #define in read()
    #define rep(i, x, y) for(int i = (x); i <= (y); i++)
    #define per(i, x, y) for(int i = (x); i >= (y); i--)
    
    typedef long long ll;
    
    const int N = 55;
    const ll inf = 0x3f3f3f3f3f3f3f3f;
    
    int n, K;
    ll c[N], L[N], R[N];
    ll dp[N][N][N][2][2][2][2];
    
    ll calc(int bit, int l, int r, int l1, int l2, int r1, int r2) {
        if(bit == K) return l > r ? 0 : inf;
        ll& res = dp[bit][l][r][l1][l2][r1][r2];
        if(~res) return res; res = inf;
        ll tl = ((l1 ? L[l - 1] : R[l - 1]) >> bit & 1ll) ^ l2, tr = ((r1 ? L[r + 1] : R[r + 1]) >> bit & 1ll) ^ r2;
        res = min(res, calc(bit + 1, l, r, l1, 0, r1, 0) + (l != 1 && r != n && tl ^ tr ? c[bit] : 0));
        rep(k, l, r)
            rep(t, 0, 1) {
            if(bit == 0) res = min(res, calc(bit, l, k - 1, l1, l2, t, 0) + calc(bit, k + 1, r, t, 0, r1, r2)); // 考虑从来没有脱离限制的情况
            ll bw = t ? L[k] : R[k];
            if(L[k] <= ((bw ^ (1ll << bit)) & (~((1ll << bit) - 1))) && ((bw ^ (1ll << bit)) | ((1ll << bit) - 1)) <= R[k]) // check
                res = min(res, calc(bit, l, k - 1, l1, l2, t, 1) + calc(bit, k + 1, r, t, 1, r1, r2));
        } return res;
    }
    
    int main() {
        n = in, K = in; rep(i, 1, n) L[i] = in, R[i] = in;
        rep(i, 0, K - 1) c[i] = in; memset(dp, -1, sizeof dp);
        printf("%lld
    ", calc(0, 1, n, 0, 0, 0, 0));
        return 0;
    }
    
    本博客作者:Werner_Yin(https://www.cnblogs.com/werner-yin/) ,转载时请注明出处,谢谢支持!
  • 相关阅读:
    SQL——索引
    const 与 readonly知多少
    ASP.NET MVC 4 RC的JS/CSS打包压缩功能
    学习IIS & MVC的运行原理
    IIS中使用ASP.NET MVC的经验总结
    cookie 和session 的区别详解
    SQL之经典语句
    SQL存储过程,使用事务(try catch),游标
    深入理解SQL的四种连接-左外连接、右外连接、内连接、全连接
    Sql效能优化总结(续)- sql语句优化篇
  • 原文地址:https://www.cnblogs.com/werner-yin/p/VP-CF1456-2021-9-13.html
Copyright © 2020-2023  润新知