Codeforces Round #753 (Div. 3) 口胡 & 乱做

主要是分三种情况:

  • 口胡一眼秒了,实现也很简单,随便写了写 / 懒得写
  • 口胡一眼秒了,感觉不大会实现,于是去瞅了瞅别人的代码
  • 没读懂题

A

直接模拟。

B

简单数学题 + 找规律,不过有点阅读理解

C

总感觉这题在哪做过,反正是简单题,把全局减法用标记存起来就好了。

D. Blue-Red Permutation

挺有意思的一个贪心,虽然也是简单题。注意到 Red 是可以变大的,那就让它尽量变大;Blue 是可以变小的,那就让它尽量变小。

const int MAXN = 2e5 + 10;

int n;
int aa[MAXN];
std::vector<int> blues, reds;
std::string s;

void _main() {
    cin >> n;
    rep (i, 1, n) {
        cin >> aa[i];
    } cin >> s;
    rep (i, 1, n) {
        if (s[i - 1] == 'B') blues.push_back(aa[i]);
        else reds.push_back(aa[i]);
    }
    bool ans = true;
    std::sort(ALL(blues)); std::sort(ALL(reds));
    // blue: decrease, place at 1 ~ siz(blue)
    int ind = 1;
    for (auto v : blues) {
        if (v < ind) ans = false;
        ++ind;
    }
    // red: increase, place at siz(blue) + 1 ~ n
    for (auto v : reds) {
        if (v > ind) ans = false;
        ++ind;
    }
    cout << (ans ? "YES" : "NO") << endl;
    blues.clear(); reds.clear();
}

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T; cin >> T;
    while (T --> 0) {
        _main();
    }
    return 0;
}

E. Robot on the Board 1

很容易想到是记录上下左右当前最远扩展了多少,然后判一判就好了。

不过感觉自己有点思维固化,最开始的想法居然是先把所有操作走完,然后一个一个回退,没想到可以直接边走边判,超出了直接输出答案即可。

F. Robot on the Board 2

看到谷区没有题解于是就怒水了一发题解

从搜索说起

首先我们看到这个题目可以很自然地想到如何暴力:对于每一个格子从当前点出发进行搜索。

但是这个过程有很多步搜索是不必要的:从一个格子出发,最后停下来的位置是一定的,那么如果这个格子被搜过一遍了,我们还继续搜它干什么呢?直接用我们已经知道的信息不就好了吗?

于是我们考虑采用记忆化的思想:记录一个 dist[i][j] 表示从位置 (i, j) 出发能执行的最大指令数。

接下来从一个未搜过的起始点开始搜索,直到我们碰见一个之前走过的格子。这个结束有三种情况:

  • 跳出地图边界
  • 走到这次搜索走过的格子,也就是成环
  • 走到以前搜索走过的格子

我们分别讨论一下:

跳出地图边界

Codeforces Round #753 (Div. 3) 口胡 & 乱做

图是拿 Excel 画的

这种情况很简单,我们照着路径顺序逆着更新 dist 即可。

Codeforces Round #753 (Div. 3) 口胡 & 乱做

走出环

Codeforces Round #753 (Div. 3) 口胡 & 乱做

这种情况下,从环上的所有点出发,都能绕环走一圈,所以环上的点答案就是环长;环外的点就照着路径顺序逆着更新 dist

Codeforces Round #753 (Div. 3) 口胡 & 乱做

走到以前走过的位置

Codeforces Round #753 (Div. 3) 口胡 & 乱做

可以发现,答案可以直接从我们之前走过的点继承过来的,照着路径顺序逆着更新 dist 即可。

Codeforces Round #753 (Div. 3) 口胡 & 乱做

代码实现

const int MAXN = 2000 + 10;

const std::string drr = "RLDU";
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};

int n, m;
char mat[MAXN][MAXN];

int dist[MAXN][MAXN];
bool vis[MAXN][MAXN];

void SearchGetPath(std::vector<std::pair<int, int> > &path, int &x, int &y) {
    while (!(x < 1 || y < 1 || x > n || y > m || vis[x][y])) {
        // DEBUG(x); DEBUG(y);
        vis[x][y] = true;
        path.push_back({x, y});
        int dr = (int) (drr.find(mat[x][y]));
        x += dx[dr]; y += dy[dr];
    }
}

void _main() {
    cin >> n >> m;
    rep (i, 1, n) cin >> (mat[i] + 1);
    rep (x, 1, n) rep (y, 1, m) {
        dist[x][y] = -1;
        vis[x][y] = 0;
    }
    rep (x, 1, n) rep (y, 1, m) if (dist[x][y] == -1) {
        int nx = x, ny = y; std::vector<std::pair<int, int> > path;
        SearchGetPath(path, nx, ny);
        if (nx < 1 || ny < 1 || nx > n || ny > m) {
            // 跳出地图边界
            path.push_back({nx, ny});
            dist[nx][ny] = 0;
            for (int i = (int) path.size() - 2; i >= 0; --i) {
                dist[path[i].fi][path[i].se] = dist[path[i + 1].fi][path[i + 1].se] + 1;
            }
        } else {
            // 找环长
            int circlesize = (int) (path.end() - std::find(ALL(path), std::make_pair(nx, ny)));
            if (circlesize == 0) {
                // 这不是环,是之前走过的路径
                path.push_back({nx, ny});
                for (int i = (int) path.size() - 2; i >= 0; --i) {
                    dist[path[i].fi][path[i].se] = dist[path[i + 1].fi][path[i + 1].se] + 1;
                }
                continue;
            }
            int i = path.size() - 1;
            int step = 1; // 一个小 trick,在环上的时候答案是 circlesize,在环外的时候答案就是 step
            for (i = path.size() - 1; i >= 0; --i) {
                dist[path[i].fi][path[i].se] = std::max(step, circlesize);
                ++step;
            }
        }
    }
    int ax = 0, ay = 0;
    rep (i, 1, n) rep (j, 1, m) {
        if (dist[i][j] > dist[ax][ay]) {
            ax = i; ay = j;
        }
    }
    cout << ax << ' ' << ay << ' ' << dist[ax][ay] << endl;
}

int main() {
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T; cin >> T;
    while (T --> 0) {
        _main();
    }
    return 0;
}

G. Banquet Preparations 1

本题还是有点阅读理解的。在看题解之前我一直没看懂题目到底在说什么,还以为是让试吃者的 balance 最小

题意就是让你求一组 \(n\) 个 \(x_i\),使得:

\[\left|\sum_{i = 1}^n(a_i - x_i) - \sum_{i = 1}^n(b_i - (m - x_i))\right| \]

这个东西最小。

先化简一下式子:

\[\left|\sum_{i = 1}^n(a_i - b_i + m) - 2\sum_{i = 1}^n x_i\right| \]

其中每一个 \(x_i\) 都有一个范围限制:

\[\max(m - b_i, 0) \leq x_i \leq \min(a_i, m) \]

左边是一个固定值,我们把它记为 \(S\);右边是一个一堆范围求和。是不是感觉好做一些了?

注意到一个性质:假如我们把所有 \(x_i\) 取到最小值的和记为 \(L\),取到最大值的和记为 \(R\),那么 \(L \leq \sum x_i \leq R\),而且 \(\sum x_i\) 可以取遍所有的 \([L, R]\)。这是初中的不等式知识,很好理解。

这个性质将题目转化为了:我们要在 \([L, R]\) 中找到最接近 \(\lfloor \frac{S}{2} \rfloor\) 的数。

此时这就是一道大水题了:对 \(S, 2L, 2R\) 分类讨论,

  • \(S \leq 2L\) 时直接将所有值取到最小;
  • \(2R \leq S\) 时直接将所有值取到最大;
  • \(S \in [2L, 2R]\) 时,如果 \(S\) 是偶数那么答案是 \(0\),否则是 \(1\)(因为一个奇数减一个偶数不可能是一个偶数),此时方案可以直接贪心的去取,先将所有的 \(x\) 保底取到最小值,然后将 \(\lfloor \frac{S}{2} \rfloor - L\) 贪心地进行分配,如果当前能取最大值就尽量取最大值。
const int MAXN = 2e5 + 10;

int n, m;
std::pair<lli, lli> dishes[MAXN];
std::pair<lli, lli> bounds[MAXN];
lli chosenx[MAXN];

void _main() {
    n = read(); m = read();
    lli s = 0;
    lli suml = 0, sumr = 0;
    rep (i, 1, n) {
        lli a = read(); lli b = read();
        dishes[i] = {a, b};
        s += (a - b + m);
        bounds[i].fi = std::max(m - b, 0ll);
        bounds[i].se = std::min((lli) m, a);
        suml += bounds[i].fi; sumr += bounds[i].se;
    }
    if (2ll * sumr <= s) {
        printf("%lld\n", s - 2ll * sumr);
        rep (i, 1, n) {
            lli x = bounds[i].se, y = m - x;
            printf("%lld %lld\n", x, y);
        }
    }
    else if (s <= 2ll * suml) {
        printf("%lld\n", 2ll * suml - s);
        rep (i, 1, n) {
            lli x = bounds[i].fi, y = m - x;
            printf("%lld %lld\n", x, y);
        }
    }
    else {
        printf("%lld\n", (s % 2));
        lli ns = (s >> 1) - suml;
        rep (i, 1, n) {
            chosenx[i] = bounds[i].fi;
            lli dtx = bounds[i].se - bounds[i].fi;
            if (ns >= dtx) {
                ns -= dtx; chosenx[i] += dtx;
            } else {
                chosenx[i] += ns; ns = 0;
            }
        }
        lli checksum = 0;
        rep (i, 1, n) {
            printf("%lld %lld\n", chosenx[i], m - chosenx[i]);
            checksum += chosenx[i];
        }
        assert(std::abs(checksum * 2 - s) <= 1);
    }
}

int main() {
    // freopen("gout.out", "w", stdout);
    std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T = read();
    while (T --> 0) {
        _main();
    }
    return 0;
}

H. Banquet Preparations 2

仍然是在看题解之后才看懂题意

不过这题我并没有看懂题解。。过几天再来填坑

上一篇:1046 Shortest Distance (20 分)


下一篇:研发产品经理的价值思考-上