wxgmjfhy

codeforeces 1004, div2, A-E

目录

cf2067

A

  • 显然 x + 1 - y 是 9 的非负整数倍时有解
inline void solve() {
    cin >> x >> y;

    int a = x + 1 - y;

    if (a % 9 == 0 && a / 9 >= 0) {
        cout << "Yes\n";
    } else {
        cout << "No\n";
    }
}

B

  • 等效于要所有的数的数量为偶数
  • 小的数只要数量 >=2, 无论奇偶, 就留下两个, 其余的都 +1 看大的数需不需要
  • 如果中途有数量为 1 的出现或者最后还有奇数, 则无解
inline void solve() {
    cin >> n;

    vector<int> a(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    vector<int> cnt(n + 2);

    for (int i = 1; i <= n; i++) {
        cnt[a[i]]++;
    }

    for (int i = 1; i <= n; i++) {
        if (cnt[i]) {
            if (cnt[i] == 1) {
                cout << "No\n";
                return;
            }

            cnt[i + 1] += cnt[i] - 2;
            cnt[i] = 2;
        }
    }

    for (auto w : cnt) {
        if (w & 1) {
            cout << "No\n";
            return;
        }
    }

    cout << "Yes\n";
}

C

  • 不知道为什么这么做: 每次选择一种数一直加到合法
int a[10] = {3, 4, 5, 6, 7, 8, 9, 0, 1, 2};

inline bool check(long long x) {
    while (x) {
        if (x % 10 == 7) return true;
        x /= 10;
    }
    return false;
}

inline void solve() {
    cin >> n;

    int ans = a[n % 10];

    for (auto w : num9) {
        if (w > n * 10) {
            break;
        }

        int cnt = 0;
        long long x = n;

        while (!check(x) && cnt < ans) {
            x += w;
            cnt++;
        }

        ans = min(ans, cnt);
    }

    cout << ans << "\n";
}

D

  • 先看 x 中是不是 1-n 都出现了
  • 如果有数字没出现, 那么如果答案是一个有向图, 则这个数字对应的点到任何点的最短路为 0, 而曼哈顿距离根据题意不可能为 0, 所以询问一次看结果是否非零即可
  • 如果 1-n 都出现了, 选择询问数字 1 出现的下标 pos1 和数字 n 出现的下标 posn
    • 如果是曼哈顿距离, 距离至少是 n-1, 因此如果询问的结果小于 n-1, 一定是有向图
    • 而 n 个点最短路不可能大于 n-1, 因此如果询问的结果大于 n-1, 一定是曼哈顿距离
    • 只剩下等于 n-1 的情况: 如果是有向图, 显然 posn 到 pos1 的最短路不可能是 n-1, 所以反过来询问一次, 结果相同则是曼哈顿距离, 否则是有向图
inline void solve() {
    cin >> n;

    vector<int> a(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    int cnt = 0;

    vector<int> vis(n + 1);

    for (int i = 1; i <= n; i++) {
        if (!vis[a[i]]) {
            cnt++;
        }
        vis[a[i]]++;
    }

    if (cnt != n) {
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                int j = 1;
                while (j == i) j++;

                int x = query(i, j);
                int y = query(j, i);

                if (x) {
                    cout << "! B" << endl;
                    return;
                } else {
                    cout << "! A" << endl;
                    return;
                }
            }
        }
    }

    int pos1 = -1, posn = -1;

    for (int i = 1; i <= n; i++) {
        if (a[i] == 1) pos1 = i;
        if (a[i] == n) posn = i;
    }

    int x = query(pos1, posn);

    if (x < n - 1) {
        cout << "! A" << endl;
        return;
    }

    if (x > n - 1) {
        cout << "! B" << endl;
        return;
    }

    int y = query(posn, pos1);

    if (x == y) {
        cout << "! B" << endl;
    } else {
        cout << "! A" << endl;
    }
}

E

  • 显然可以取所有正数, 记正数有 cnt 个

  • 显然不能取两个 0

  • 那么答案至少为 cnt, 至多为 cnt+1, 现在就看能不能放入一个 0

  • 计算出正数的前缀 min, 以及假设后缀中初始就有一个 0 的后缀 mex

    for (int i = 1; i <= n; i++) {
        pre[i] = min(pre[i - 1], (a[i] == 0 ? inf : a[i]));
    }
    
    for (int i = n, x = 1; i >= 1; i--) {
        if (a[i] < n) vis[a[i]] = 1;
        while (vis[x]) x++;
        suf[i] = x;
    }
    
  • 如果放入一个 0, 那么从 0 开始到最后的部分不需要检查, 因为始终前缀 min 等于 0, 后缀 mex 也等于 0

  • 所以只检查 0 前面的部分: 如果存在 i, a[i] == 0, 且对于所有 1<=t<i, pre[t]>=suf[t+1], 说明可以放入 a[i] 这个 0

    for (int i = 1; i <= n; i++) {
        if (a[i] == 0) {
            cout << cnt + 1 << "\n";
            return;
        }
    
        if (i < n && pre[i] < suf[i + 1]) {
            break;
        }
    }
    
  • 完整代码

inline void solve() {
    cin >> n;

    vector<int> a(n + 1);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    int cnt = n - count(a.begin() + 1, a.end(), 0);

    const int inf = 1e9 + 1;

    vector<int> pre(n + 1, inf), suf(n + 1), vis(n + 1);

    for (int i = 1; i <= n; i++) {
        pre[i] = min(pre[i - 1], (a[i] == 0 ? inf : a[i]));
    }

    for (int i = n, x = 1; i >= 1; i--) {
        if (a[i] < n) vis[a[i]] = 1;
        while (vis[x]) x++;
        suf[i] = x;
    }

    for (int i = 1; i <= n; i++) {
        if (a[i] == 0) {
            cout << cnt + 1 << "\n";
            return;
        }

        if (i < n && pre[i] < suf[i + 1]) {
            break;
        }
    }

    cout << cnt << "\n";
}