W
H
X

AtCoder Grand Contest 001 CDEF

AGC001

C - Shorten Diameter

枚举直径的中点。把距离过大的删掉。奇数个点枚举中间的边

#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 = 2010;
int n, k, lim, res = 1e9, a[N], b[N]; vector<int> g[N];
#define pb push_back
int dfs (int u, int la, int d) {
    int s = (d > lim);
    for (int v : g[u]) if (v != la)
        s += dfs (v, u, d + 1); return s;
}
signed main() {
    read (n), read (k); lim = k / 2;
    for (int i = 1, u, v; i < n; ++i)
        read (a[i]), read (b[i]), g[a[i]].pb (b[i]), g[b[i]].pb (a[i]);
    if (k & 1) {
        for (int i = 1; i < n; ++i)
            res = min (res, dfs (a[i], b[i], 0) + dfs (b[i], a[i], 0));
    } else {
        for (int i = 1; i <= n; ++i)
            res = min (res, dfs (i, i, 0));
    }
    return printf ("%d\n", res), 0;
}

D - Arrays and Palindrome

在两个位置之间连边表示字符相同。一个回文串相当于从中间向两边对称的连边。所有字符相等就是所有点连通。

\(n\) 个节点连通至少要 \(n - 1\) 条边。奇回文连 \(\frac{len}{2} - 0.5\) ,偶回文连 \(\frac{len}{2}\)。所有都是偶回文只有 \(\frac{n}{2}\times 2 = n\) 条边。如果 \(A\) 中有多于两个奇数,最多只有 \(n - 1.5\) 条边,不可能连通。接下来只要对奇数个数 \(\leq 2\) 的构造出合法方案。设构造序列为 \(b\)

如果全部都是偶数,\(b_1 = 1\),后面的 \(b_i= a_{i-1}\),注意最后一个要 \(-1\),其实就是和 \(a\) 错位把所有的串在一起

如果有一个奇数,把这个放到末尾,其他不变。

两个奇数,一头一尾,此时 \(b_i = a_i\),第一个加一,最后一个减一。

#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, m, a[N], tot, res[N];
signed main() {
    read (n), read (m); int s = 0;
    for (int i = 1; i <= m; ++i) read (a[i]);
    for (int i = 1; i <= m; ++i) s += (a[i] & 1);
    if (s > 2) return puts ("Impossible"), 0;
    if (s == 0) {
        res[++tot] = 1;
        for (int i = 1; i < m; ++i) res[++tot] = a[i];
        if (a[m] > 1) res[++tot] = a[m] - 1;
    } else if (s == 1) {
        for (int i = 1; i <= m; ++i)
            if (a[i] & 1) { swap (a[i], a[m]); break; }
        res[++tot] = 1;
        for (int i = 1; i < m; ++i) res[++tot] = a[i];
        if (a[m] > 1) res[++tot] = a[m] - 1;
    } else {
        for (int i = 1, t = 0; i <= m; ++i) {
            if (a[i] & 1) {
                if ((++t) == 1) swap (a[i], a[1]);
                else { swap (a[i], a[m]); break; }
            }
        }
        res[++tot] = a[1] + 1;
        for (int i = 2; i < m; ++i) res[++tot] = a[i];
        if (a[m] > 1) res[++tot] = a[m] - 1;
    }
    for (int i = 1; i <= m; ++i) printf ("%d ", a[i]); puts ("");
    printf ("%d\n", tot);
    for (int i = 1; i <= tot; ++i) printf ("%d ", res[i]);
    return 0;
}

E - BBQ Hard

\(C_{a_i+b_i+a_j+b_j}^{a_i+a_j}\) 的集合意义就是从 \((0,0)\) 走到 \((a_i+a_j,b_i+b_j)\) 的方案。但这个和 \(i,j\) 均相关,不好计算。不妨把坐标轴移动,看成从 \((-a_i,-b_i)\) 走到 \((a_j,b_j)\) 的方案数。

可以先把所有起点的的数量统计,然后按照普通的 \(dp\) 做一遍。\(f_{i,j} = f_{i-1,j} + f_{i,j-1}\)

#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 = 2e5 + 5, M = 2010, mod = 1e9 + 7;
int n, res, pw[N], in[N], f[M << 1][M << 1], p[M << 1][M << 1];
int qpow (int x, int y) {
    int t = 1;
    while (y) {
        if (y & 1) t = t * x % mod;
        x = x * x % mod, y >>= 1;
    } return t;
}
#define up(x, y) ((x += y) >= mod && (x -= mod))
int C (int x, int y) {
    return pw[x] * in[y] % mod * in[x - y] % mod;
}
signed main() {
    read (n); pw[0] = 1;
    for (int i = 1; i <= 8000; ++i) pw[i] = pw[i - 1] * i % mod;
    in[8000] = qpow (pw[8000], mod - 2);
    for (int i = 8000; i; --i) in[i - 1] = in[i] * i % mod;
    for (int i = 1, x, y; i <= n; ++i) {
        read (x), read (y), ++f[2000 - x][2000 - y], ++p[2000 + x][2000 + y];
        up (res, mod - C (x + x + y + y, x + x));
    }
    for (int i = 0; i <= 4000; ++i)
        for (int j = 0; j <= 4000; ++j) {
            if (i) up (f[i][j], f[i - 1][j]);
            if (j) up (f[i][j], f[i][j - 1]);
            (res += p[i][j] * f[i][j]) %= mod;
        }
    return printf ("%lld\n", res * qpow (2, mod - 2) % mod), 0;
}

F - Wide Swap

题目的条件特别别扭,可以弄到逆序列上化简。算出 \(a\),满足 \(a_{p_i}=i\)。题目变成如果相邻的两个数 \(|a_i-a_{i+1}|>=k\),可以交换,使 \(a\) 的字典序最小。

如果两个数的绝对值之差 \(<k\),相对位置一定不变。暴力做法可以在所有这样的数对之间连有向边,表示相对位置关系,按照拓扑序贪心。但很多边是不需要的,只需要连差值在范围内且位置最靠近的数(一大一小两条)。可以手算理解一下正确性。需要连的边可以用线段树求出

#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 = 5e5 + 5;
int n, k, a[N], w[N], cnt[N]; vector<int> g[N];
struct tree {
    int c[N << 2];
    #define ls (p << 1)
    #define rs (p << 1 | 1)
    void modify (int p, int l, int r, int i) {
        c[p] = max (c[p], i); if (l == r) return;
        int mid (l + r >> 1);
        if (w[i] <= mid) modify (ls, l, mid, i);
        else modify (rs, mid + 1, r, i);
    }
    int query (int p, int l, int r, int ql, int qr) {
        if (ql <= l && r <= qr) return c[p];
        int mid (l + r >> 1), res = 0;
        if (ql <= mid) res = query (ls, l, mid, ql, qr);
        if (qr > mid) res = max (res, query (rs, mid + 1, r, ql, qr));
        return res;
    }
} t;
priority_queue<int> q; int res[N];
signed main() {
    read (n), read (k);
    for (int i = 1; i <= n; ++i) read (a[i]), w[a[i]] = i;
    for (int i = 1, tmp; i <= n; ++i) {
        // [p[i] - k + 1, p[i]] 、[p[i], p[i] + k - 1] 内最近的值,
        tmp = t.query (1, 1, n, w[i] - k + 1, w[i]);
        if (tmp) g[w[i]].push_back (w[tmp]), ++cnt[w[tmp]];
        tmp = t.query (1, 1, n, w[i], w[i] + k - 1);
        if (tmp) g[w[i]].push_back (w[tmp]), ++cnt[w[tmp]];
        t.modify (1, 1, n, i);
    }
    for (int i = 1; i <= n; ++i) if (!cnt[i]) q.push (i);
    int now = n;
    while (now) {
        int u = q.top(); q.pop(); res[u] = now, --now;
        for (int v : g[u]) if ((--cnt[v]) == 0) q.push (v);
    }
    for (int i = 1; i <= n; ++i) printf ("%d\n", res[i]);
    return 0;
}
posted @ 2020-12-24 16:16  -敲键盘的猫-  阅读(177)  评论(0编辑  收藏  举报