W
H
X

AtCoder Grand Contest 002 DEF

AGC002

D - Stamp Rally

多次询问,整体二分。具体一些,当前二分的区间为 \(l,r\),先设定答案为 \(mid\),判断是否可行,可行的往 \([l,mid]\) 递归,否则走 \([mid + 1, r]\)。关键在于如何快速判定是否可行。

用并查集维护连通块的大小,一种办法使用可回退并查集。还有一种是按照 \(bfs\) 顺序处理区间(按层处理)。这样只要在每层开头暴力清空,然后按照顺序连

#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, q, res[N];
struct query { int x, y, z; } a[N];
struct edge { int x, y; } e[N];
int fa[N], sz[N];
int find (int x) { return fa[x] == x ? x : fa[x] = find (fa[x]); }
void merge (int x, int y) {
    int fx = find (x), fy = find (y);
    if (sz[fx] > sz[fy]) swap (fx, fy);
    if (fx ^ fy) fa[fx] = fy, sz[fy] += sz[fx];
}
int cnt, ll[N << 2], rr[N << 2], tag[N << 2];
vector<int> g[N << 2];
#define ls (p << 1)
#define rs (p << 1 | 1)
void build (int p, int l, int r) {
    ll[p] = l, rr[p] = r, tag[p] = 1;
    if (l == r) return; int mid (l + r >> 1);
    build (ls, l, mid), build (rs, mid + 1, r);
}
void solve () {
    for (int p = 1; p <= (n << 2); ++p) {
        if (!tag[p]) continue;
        // printf ("%d %d\n", ll[p], rr[p]);
        if (ll[p] == 1) {
            for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
        }
        if (ll[p] == rr[p]) {
            merge (e[ll[p]].x, e[ll[p]].y);
            for (int i : g[p]) res[i] = ll[p]; continue;
        }
        int mid (ll[p] + rr[p] >> 1);
        for (int i = ll[p]; i <= mid; ++i) merge (e[i].x, e[i].y);
        for (int i : g[p]) {
            int x = a[i].x, y = a[i].y, z = a[i].z;
            x = find (x), y = find (y);
            int s = (x == y) ? sz[x] : sz[x] + sz[y];
            // printf ("%d %d %d\n", i, s, z);
            s >= z ? g[ls].push_back (i) : g[rs].push_back (i);
        }
        for (int i = mid + 1; i <= rr[p]; ++i) merge (e[i].x, e[i].y);
    }
}
signed main() {
    read (n), read (m);
    for (int i = 1; i <= m; ++i) read (e[i].x), read (e[i].y);
    read (q);
    for (int i = 1; i <= q; ++i)
        read (a[i].x), read (a[i].y), read (a[i].z);
    for (int i = 1; i <= q; ++i) g[1].push_back (i);
    build (1, 1, m); solve ();
    for (int i = 1; i <= q; ++i) printf ("%d\n", res[i]);
    return 0;
}

E - Candy Piles

uxC5ND.png

uxCT9H.png

剽三张图助于理解。把所有石堆排序扔到坐标轴上后事情就很明显了。两种操作分别代表向上走一步和向右走一步。最外围一圈的胜负状态确定。

可以发现,先手可以走到任意的 \((x,y),|x-y|\leq1\),后手可以控制 \(|x-y|\leq1\),所以只要判断是否存在这样的 \((x,y)\) 让先手胜,不胜则败。

#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;
int n, tag, a[N];
void work () {
    for (int i = 1; i <= n; ++i) {
        int j = i + 1;
        while (a[i] == a[j] && j <= n) ++j; --j;
        int l = i, r = j;
        if ((r - a[i]) & 1) {
            if (l <= a[i] - 1 && a[i] - 1 <= r) { tag = 1; break; }
            if (l <= a[i] + 1 && a[i] + 1 <= r) { tag = 1; break; }
        } i = j;
        l = a[i + 1] + 1, r = a[i];
        if (((r - i) & 1)) {
            if (l <= i - 1 && i - 1 <= r) { tag = 1; break; }
            if (l <= i + 1 && i + 1 <= r) { tag = 1; break; }
        }
    }
}
signed main() {
    read (n);
    for (int i = 1; i <= n; ++i) read (a[i]);
    sort (a + 1, a + n + 1), reverse (a + 1, a + n + 1);
    work ();
    puts (tag ? "First" : "Second");
    return 0;
}

F - Leftmost Ball

因为每种颜色的求会有一个白色,所以可以重新把球分类为白球和其他颜色,这两种球相对独立

这就是状态了:\(f_{i,j}\) 表示当前填了 \(i\) 个白球,\(j\) 类其他颜色的球的方案数 (其他颜色都是一下全部填完)

转移依据也比较奇特:按照当前空位最左边填什么转移。这样可以保证白球的合法性

如果填白的直接转,否则乘上一个组合数。当然其他颜色种类不能大于白球数量。组合数具体是什么不难想,就不写了(懒)

#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 = 2010, mod = 1e9 + 7;
int n, k, f[N][N], pw[N * N], in[N * N];
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;
}
int C (int x, int y) {
    // if (x < y) return 0;
    return pw[x] * in[y] % mod * in[x - y] % mod;
}
signed main() {
    read (n), read (k); f[0][0] = pw[0] = 1;
    for (int i = 1; i <= n * k; ++i) pw[i] = pw[i - 1] * i % mod;
    in[n * k] = qpow (pw[n * k], mod - 2);
    for (int i = n * k; i >= 1; --i) in[i - 1] = in[i] * i % mod;
    if (k == 1) return puts ("1"), 0;
    for (int i = 1; i <= n; ++i)
        for (int j = 0; j <= i; ++j) {
            f[i][j] = f[i - 1][j];
            if (j) (f[i][j] += f[i][j - 1] * (n - j + 1) % mod * C (n * k - i - (j - 1) * (k - 1) - 1, k - 2)) %= mod;
        }
    return printf ("%lld\n", f[n][n]), 0;
}
posted @ 2020-12-24 16:29  -敲键盘的猫-  阅读(100)  评论(0编辑  收藏  举报