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 \times \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 \times \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 \ne 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\n", calc(0, 1, n, 0, 0, 0, 0));
    return 0;
}
posted @ 2021-09-14 15:07  Werner_Yin  阅读(94)  评论(1编辑  收藏  举报