W
H
X

AtCoder Grand Contest 004 CDEF

AGC004

C - AND Grid

构造题。染色方案要求四联通,边界的行、列提供了一个很好的连通介质,且题目保证给定网格图中边界都为 \(.\)

具体做法:第一张图中把最左边一列涂黑,第二张图把最右边一列涂黑,当然必须涂黑的两张图中都涂黑。然后第一张图中把偶数行全部涂黑,第二张图把奇数行全部涂黑,既满足了连通性,又不会与条件冲突

#include <bits/stdc++.h>
using namespace std;
const int N = 505;
int h, w; char c[N][N], a[N][N], b[N][N];
signed main() {
    scanf ("%d %d", &h, &w);
    for (int i = 1; i <= h; ++i)
        scanf ("%s", c[i] + 1);
    for (int i = 1; i <= h; ++i)
        for (int j = 1; j <= w; ++j) a[i][j] = b[i][j] = c[i][j];
    for (int i = 1; i <= h; ++i) a[i][1] = '#', b[i][w] = '#';
    for (int i = 1; i <= h; i += 2)
        for (int j = 2; j < w; ++j) a[i][j] = '#';
    for (int i = 2; i <= h; i += 2)
        for (int j = 2; j < w; ++j) b[i][j] = '#';
    for (int i = 1; i <= h; ++i) {
        for (int j = 1; j <= w; ++j) putchar (a[i][j]); puts ("");
    } puts ("");
    for (int i = 1; i <= h; ++i) {
        for (int j = 1; j <= w; ++j) putchar (b[i][j]); puts ("");
    }
    return 0;
}

D - Teleporter

原图构成一棵基环内向树。所有点到 \(1\) 的距离要相同,可以想象,一定是先跳到 \(1\) 然后在原地跳若干次(可以为 \(0\)),那么 \(1\) 必须连向自己。还有一个条件是所有点到 \(1\) 的距离不能 \(>k\),贪心的进行一遍 \(dfs\)\(1\) 连向自己,此时已经变成一棵树),当发现距离(深度)不满足条件就把答案 \(+1\),然后把深度改成 \(1\)

代码中的分类讨论应该是多次一举,不必理会

#include <bits/stdc++.h>
using namespace std;
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5;
int n, k, res, d[N], a[N]; vector<int> g[N];
void dfs (int u, int dp) {
    d[u] = dp;
    for (int v : g[u]) dfs (v, dp + 1), d[u] = max (d[u], d[v]);
    if (d[u] - dp == k - 1 && d[u] > k) ++res, d[u] = 0;
}
signed main() {
    read (n), read (k);
    for (int i = 1; i <= n; ++i) read (a[i]);
    if (k == 1) {
        for (int i = 1; i <= n; ++i) if (a[i] != 1) ++res;
    } else {
        if (a[1] != 1) ++res, a[1] = 1;
        for (int i = 2; i <= n; ++i)
            g[a[i]].push_back (i); dfs (1, 0);
    }
    return printf ("%d\n", res), 0;
}

E - Salvage Robots

非常“暴力”的动态规划。可以把机器人走动的过程想成一个框在动,机器人不动。如果机器人出框就挂了,和框的中心 \(E\) 重合就 \(win\) 了。\(f_{l,r,u,d}\) 表示框向左最多移动 \(l\),(向右向上向下同理)能获得的最大分数,但是转移的情况比较复杂就算了吧。。。不如看这个

#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int n, m; char ch[N][N]; short res, f[N][N][N][N], p[N][N], q[N][N];
#define now f[a][b][c][d]
#define down f[a + 1][b][c][d]
#define up f[a][b][c + 1][d]
#define right f[a][b + 1][c][d]
#define left f[a][b][c][d + 1]
signed main() {
    cin >> n >> m; int x = 0, y = 0;
    for (int i = 1; i <= n; ++i) scanf ("%s", ch[i] + 1);
    for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j) {
        if (ch[i][j] == 'E') x = i, y = j;
        p[i][j] = p[i][j - 1] + (ch[i][j] == 'o');
        q[i][j] = q[i - 1][j] + (ch[i][j] == 'o');
    }
    for (int a = 0; a <= n - x; ++a)         // 向下
    for (int b = 0; b <= m - y; ++b)         // 向右
    for (int c = 0; c < x; ++c)              // 向上
    for (int d = 0; d < y; ++d) {            // 向左
        res = max (res, now);
        if (x + a < n - c) down = max ((int)down, (int)now + p[x + a + 1][min (m - d, y + b)] - p[x + a + 1][max (b, y - d - 1)]);
        if (x - c > a + 1) up = max ((int)up, (int)now + p[x - c - 1][min (m - d, y + b)] - p[x - c - 1][max (b, y - d - 1)]);
        if (y + b < m - d) right = max ((int)right, (int)now + q[min (n - c, x + a)][y + b + 1] - q[max (a, x - c - 1)][y + b + 1]);
        if (y - d > b + 1) left = max ((int)left, (int)now + q[min (n - c, x + a)][y - d - 1] - q[max (a, x - c - 1)][y - d - 1]);
    }
    cout << res << '\n';
}

F - Namori

“把相同颜色的相邻点反转”就很可怕,不如先考虑树的情况

有一个有意思的转换:把树二分染色(为了区分,染成红蓝色),然后假设初始的时候每个红格里有一个球。每一步操作可以把这个球推向相邻的空格子

为什么等价呢?把红格里有球看成原题中的白,红格无球看成黑,蓝格有球为黑,无球为白。每个状态和条件都是等价的。最终状态是所有白格有球,黑格无球

转换后的问题没有那么恐怖。对每一条由 \(x\) 连向父亲的边进行考虑,发生在这条边上的移动次数至少是 \(|num(黑格数)-num(白格数)|\),因为数量都匹配不上肯定要进出。然后这移动次数为 \(|num(黑格数)-num(白格数)|\) 的方案也是可以精细构造出来的,树的情况就搞定啦

基环树多出来了一条边。对这个环的长度奇偶讨论

环长为偶数:此时仍然是二分图把环取出来,设环上每条边的流量为 \(x_i\),每个点进出平衡,可以列出方程组。woc这个东西相当于数轴上有 \(m\) 个村庄,要选一个点使到所有村庄的距离和最小,然后带入中间那个就好了。

环长为奇数:此时多出来的边连接的两个点颜色相同,如果在这条边上操作一次就是同时产生两个球或吃掉两个球。前面的情况如果黑白格数量不同一定无解,但这种情况同奇偶就可以。先进行多补少添的操作,然后和树的差不多,改一下环上点的值

#include <bits/stdc++.h>
using namespace std;
#define int long long
void read (int &x) {
    char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 1e5 + 5, M = N << 1;
int n, m, res, rt, qwq, c[N], f[N], fa[N];
int cnt, h[N], to[M], nxt[M], p, a[N];
void add (int u, int v) {
    to[++cnt] = v, nxt[cnt] = h[u], h[u] = cnt;
}
int get (int x) {
    return f[x] == x ? x : f[x] = get (f[x]);
}
void dfs (int u, int co) {
    for (int i = h[u], v; i; i = nxt[i]) {
        if ((v = to[i]) != fa[u]) fa[v] = u, dfs (v, -co), c[u] += c[v];
    } c[u] += co;
}
signed main() {
    read (n), read (m); rt = 1;
    for (int i = 1; i <= n; ++i) f[i] = i;
    for (int i = 1, u, v, fu, fv; i <= m; ++i) {
        read (u), read (v); fu = get (u), fv = get (v);
        if (fu == fv) rt = u, qwq = v;
        else add (u, v), add (v, u), f[fv] = fu;
    }
    dfs (rt, 1);
    if (m == n) {
        for (int i = qwq; i; i = fa[i]) a[++p] = c[i];
        if (p & 1) {
            if (c[rt] & 1) return puts ("-1"), 0;
            for (int i = qwq; i; i = fa[i]) c[i] -= c[rt] >> 1;
        } else {
            if (c[rt]) return puts ("-1"), 0;
            sort (a + 1, a + p + 1);
            for (int i = qwq; i; i = fa[i]) c[i] -= a[p >> 1];
        }
    } else if (c[rt]) return puts ("-1"), 0;
    for (int i = 1; i <= n; ++i) res += abs (c[i]);
    return printf ("%lld\n", res), 0;
}
posted @ 2021-01-12 21:07  -敲键盘的猫-  阅读(122)  评论(0编辑  收藏  举报