Featured image of post Codeforces Round #1055(Div.1+2)

Codeforces Round #1055(Div.1+2)

B

题目大意:Doran 和 Krug 正在一个由 $(n + 1) \times (n + 1)$ 个格子组成的网格上玩游戏,网格上的每个单元格坐标是从 $0$ 到 $n$(包含 $0$ 和 $n$)的整数对。Krug 的目标是尽可能长时间不被 Doran 抓住,而 Doran 的目标是尽快抓住 Krug。当 Doran 和 Krug 站在同一个格子上时,称 Doran 抓住了 Krug。游戏规则如下,Krug 和 Doran 轮流行动,Krug 先手: - Krug 可以选择留在原地,或者移动到上下左右相邻的格子(不可斜向移动)。

数据范围:$(1 \le t \le 10^4)$,$(1 \le n \le 10^9, 0 \le r_K, c_K, r_D, c_D \le n, (r_K, c_K) \ne (r_D, c_D))$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using i128 = __int128_t;
void solve() {
    ll n, st1, fs1, st2, fs2;
    cin >> n >> st1 >> fs1 >> st2 >> fs2;
    if (st1 == st2 && fs1 == fs2)
        cout << 0 << endl;
    else if (st1 == st2)
        cout << (fs1 > fs2 ? n - fs2 : fs2) << endl;
    else if (fs1 == fs2)
        cout << (st1 > st2 ? n - st2 : st2) << endl;
    else {
        ll tem = (fs1 > fs2 ? n - fs2 : fs2);
        ll tem2 = (st1 > st2 ? n - st2 : st2);
        cout << max(tem, tem2) << endl;
    }
    return;
}

C

题目大意:Keria 厌倦了为远程输出型英雄提供支援,现在她设计了一个关于支持区间查询的数据结构问题。对于一个长度为 $m$ 的数组 $b = [b_1, b_2, \ldots, b_m]$,其中 $b_i=0$ 或 $b_i=1$,定义如下的“三元组移除”操作: 1. 选择三个下标 $1 \le i < j < k \le m$,使得这三个位置上的元素相同(即 $b_i = b_j = b_k$)。2. 将这三个元素从数组中移除。该操作的代价为 $\min(k-j, j-i)$。移除后,剩余的数组连接起来,重新编号。我们的目标是通过三元组移除操作,将数组 $b$ 变为空。数组的总代价定义为:将数组清空所需一系列三元组移除操作代价之和的最小值。

数据范围:$1 \le t \le 10^4$,$1 \le n, q \le 250\,000$,$1 \le l_i \le r_i \le n$,$\sum n \le 250\,000$,$\sum q \le 250\,000$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using i128 = __int128_t;
void solve() {
    ll n, q, x, y;
    cin >> n >> q;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vl pre(n);
    pre[0] = a[0];
    rep(i, 1, n - 1) pre[i] = pre[i - 1] + a[i];
    vl pre2(n);
    pre2[0] = 0;
    rep(i, 1, n - 1) pre2[i] = pre2[i - 1] + (a[i] == a[i - 1]);
    rep(i, 0, q - 1) {
        cin >> x >> y;
        x--, y--;
        ll tem = (x == 0 ? 0 : pre[x - 1]);
        ll tem2 = pre[y] - tem;
        if (tem2 % 3 != 0 || (y - x + 1 - tem2) % 3 != 0) {
            cout << -1 << endl;
            continue;
        }
        ll tem3 = pre2[x];
        ll tem4 = pre2[y] - tem3;
        cout << ((y - x + 1) / 3) + (tem4 == 0) << endl;
    }
    return;
}

D

题目大意:对于一个长度为 $m$ 的数组 $b=[b_1,b_2,\ldots,b_m]$($b_i \geq 2$),考虑由 Poby 和 Rekkles 进行的如下二人游戏: - 两位玩家轮流行动,Poby 先手。- 在 Poby 的回合,他必须选择一个 $x \ge 2$ 的元素,将其替换为 $\left\lfloor \frac{x}{2} \right\rfloor$。即选择 $i$($1 \leq i \leq m$)且 $b_i \ge 2$,然后执行 $b_i := \left\lfloor \frac{b_i}{2} \right\rfloor$。

数据范围:$1 \le t \le 10^4$,$1 \le n, q \le 250\,000$,$2 \le a_i \le 10^9$,$1 \le l_j \le r_j \le n$,$\sum n \le 250\,000$,$\sum q \le 250\,000$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using i128 = __int128_t;
void solve() {
    ll n, q, x, y;
    cin >> n >> q;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vl b(n);
    rep(i, 0, n - 1) {
        if ((a[i] & (a[i] - 1)) == 0)
            b[i] = 1;
        else if (((a[i] - 2) & (a[i] - 1)) == 0)
            b[i] = 2;
        else
            b[i] = 3;
    }
    vl pre(n);
    vl pre2(n);
    vl pre3(n);
    pre[0] = 1LL * log2(a[0]);
    pre2[0] = (b[0] == 2);
    pre3[0] = (b[0] == 3);
    rep(i, 1, n - 1) {
        pre[i] = pre[i - 1] + 1LL * log2(a[i]);
        pre2[i] = pre2[i - 1] + (b[i] == 2);
        pre3[i] = pre3[i - 1] + (b[i] == 3);
    }
    rep(i, 0, q - 1) {
        cin >> x >> y;
        x--, y--;
        ll tem = (x == 0 ? 0 : pre[x - 1]);
        ll tem2 = (x == 0 ? 0 : pre2[x - 1]);
        ll tem3 = (x == 0 ? 0 : pre3[x - 1]);
        cout << pre[y] - tem + (pre2[y] - tem2) / 2 + (pre3[y] - tem3) << endl;
    }
    return;
}

E

题目大意:这是一个交互题。Faker 又在调皮了。你让他出一道好玩的查询题,结果他却出了一道需要你来和他互动的题。Faker 把一个排列藏了起来,而需要通过与他的互动来推断出一些有趣的信息。给定一个整数 $n$。Faker 藏了一个长度为 $n^2+1$ 的排列 $p_1, p_2, \ldots, p_{n^2+1}$。你的目标是找出这个隐藏排列中长度恰好为 $n+1$ 的单调子序列(递增或递减皆可)。可以证明,任意长度为 $n^2+1$ 的排列都必然包含一个长度为 $n+1$ 的单调子序列。关于这个证明的更多信息,你可以参考维基百科页面。

数据范围:$1 \le t \le 5000$,$1 \le n \le 100$,$\sum (n^2+1) \le 10\,001$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
vl ask(ll k, vl& res) {
    cout << "? " << k << ' ';
    rep(i, 0, k - 1) cout << res[i] << ' ';
    cout << '\n';
    cout.flush();
    ll op;
    cin >> op;
    vl res2(op);
    rep(i, 0, op - 1) cin >> res2[i];
    return res2;
}
void report(vl& res) {
    cout << "! ";
    rep(i, 0, sz(res) - 1) cout << res[i] << ' ';
    cout << '\n';
    cout.flush();
}
void solve() {
    ll n;
    cin >> n;
    vl tem;
    rep(i, 1, n * n + 1) tem.push_back(i);
    vl pa(n * n + 5);
    rep(i, 1, n) {
        vl op = ask(sz(tem), tem);
        if (sz(op) >= n + 1) {
            vl res;
            rep(i, 0, n) res.push_back(op[i]);
            report(res);
            return;
        }
        int m = sz(op);
        int l = 0, las = 0;
        for (auto& p : tem) {
            if (l <= m - 1 && op[l] == p)
                las = p, l++;
            else
                pa[p] = las;
        }
        map<ll, ll> ma;
        rep(i, 0, m - 1) ma[op[i]]++;
        vl ntem;
        for (auto& p : tem) {
            if (ma.count(p)) continue;
            ntem.push_back(p);
        }
        tem = ntem;
    }
    vl res;
    ll tem2 = tem[0];
    rep(i, 0, n) {
        res.push_back(tem2);
        tem2 = pa[tem2];
    }
    ranges::reverse(res);
    report(res);
    return;
}

F

题目大意:Zeus 正在分析战斗录像,以了解对手的攻击模式。对手有一个特殊能力:如果在 $z$ 时间内命中同一个目标三次,他的第三次攻击就会变得非常强力。为了避免被对手触发强化攻击,Zeus 不能让对手在 $z$ 时间内连续命中三次。设 $Y = \{y_1, y_2, \ldots, y_m\}$ 为包含 $m$ 个时间戳的多重集,每个 $y_i$ 代表对手攻击命中的时刻。我们称 $Y$ 是安全的,当且仅当对任意三个时间戳 $\{y_i, y_j, y_k\}$($1 \le i < j < k \le m$),都有 $\max(y_i, y_j, y_k) - \min(y_i, y_j, y_k) > z$,其中 $z$ 是给定的时间窗口。

数据范围:$1 \le t \le 20000$,$1 \le n \le 250000$,$1 \le z \le 10^9$,$1 \le x_i \le 10^9$,$1 \le q \le 250000$,$1 \le l \le r \le n$,$\sum n \le 250000$,$\sum q \le 250000$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
using i128 = __int128_t;
const ll MX = 20;
void solve() {
    ll n, q, x, y, z;
    cin >> n >> z;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    vl pa(n + 1, n);
    vvl up(n + 1, vl(MX, n));
    ll r = 0;
    rep(l, 0, n - 1) {
        while (r <= n - 1 && a[r] <= a[l] + z) r++;
        pa[l] = r;
    }
    pa[n] = n;
    rep(i, 0, n) up[i][0] = pa[i];
    rep(j, 1, MX - 1) {
        rep(i, 0, n) {
            ll mid = up[i][j - 1];
            up[i][j] = up[mid][j - 1];
        }
    }
    vl dep(n + 1);
    dep[n] = 0;
    frep(i, n - 1, 0) dep[i] = dep[pa[i]] + 1;
    auto lca = [&](ll u, ll v) {
        if (dep[u] > dep[v]) swap(u, v);
        ll d = dep[v] - dep[u];
        rep(j, 0, MX - 1) {
            if ((d >> j) & 1) v = up[v][j];
        }
        if (u == v) return u;
        frep(j, MX - 1, 0) {
            if (up[u][j] != up[v][j]) {
                u = up[u][j];
                v = up[v][j];
            }
        }
        return up[u][0];
    };
    vector<vector<pll>> up2(n + 1, vector<pll>(MX, {n, 0}));
    rep(i, 0, n - 1) {
        ll tem = lca(i, i + 1);
        up2[i][0].first = tem;
        up2[i][0].second = dep[i] + dep[i + 1] - 2 * dep[tem];
    }
    rep(j, 1, MX - 1) {
        rep(i, 0, n) {
            ll mid = up2[i][j - 1].first;
            up2[i][j].first = up2[mid][j - 1].first;
            up2[i][j].second = up2[i][j - 1].second + up2[mid][j - 1].second;
        }
    }
    auto calc = [&](ll x, ll y) {
        ll tem = 1;
        frep(i, MX - 1, 0) {
            if (up[x][i] <= y) {
                x = up[x][i];
                tem += (1LL << i);
            }
        }
        return tem;
    };
    cin >> q;
    rep(i, 0, q - 1) {
        cin >> x >> y;
        x--, y--;
        if (y - x + 1 <= 2) {
            cout << y - x + 1 << endl;
            continue;
        }
        ll tem = 0;
        frep(i, MX - 1, 0) {
            if (up2[x][i].first <= y) {
                tem += up2[x][i].second;
                x = up2[x][i].first;
            }
        }
        if (x <= y) tem += calc(x, y);
        if (x + 1 <= y) tem += calc(x + 1, y);
        cout << tem << endl;
    }
    return;
}