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/) ,转载时请注明出处,谢谢支持!
原文地址:https://www.cnblogs.com/werner-yin/p/VP-CF1456-2021-9-13.html