• 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
    ", res), 0;
    }
    

    D - Arrays and Palindrome

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

    (n) 个节点连通至少要 (n - 1) 条边。奇回文连 (frac{len}{2} - 0.5) ,偶回文连 (frac{len}{2})。所有都是偶回文只有 (frac{n}{2} imes 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
    ", 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
    ", 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
    ", res[i]);
        return 0;
    }
    
  • 相关阅读:
    docker-compose常用命令-详解
    Docker的4种网络模式
    docker-compose常用命令-详解
    windows 10安装nodejs(npm,cnpm),在谷歌浏览器上安装vue开发者工具 vue Devtools
    @Pointcut注解
    leetcode做题总结
    win10右键添加在此处打开powershell
    怎样从 bat 批处理文件调用 PowerShell 脚本
    Android Google Play app signing 最终完美解决方式
    526. Beautiful Arrangement
  • 原文地址:https://www.cnblogs.com/whx666/p/14184702.html
Copyright © 2020-2023  润新知