Featured image of post Codeforces Round #1040(Div.2)

Codeforces Round #1040(Div.2)

B

题目大意:有一个由数值 $0$、$1$、$2$ 和一个整数 $s$ 构成的数组 $a_1, a_2, \ldots, a_n$。保证数组中至少有一个 $0$,一个 $1$ 和一个 $2$。爱丽丝想要从下标 $1$ 开始向左或向右移动几步(每一步的距离为 $1$),最终达到点 $n$。当爱丽丝移动的时候,她计算她经过的格子中的数值和,并且,她想要使得结束时的和恰好等于 $s$。

数据范围:$1 \le t \le 10^3$,$3 \le n \le 50$,$0 \le s \le 1000$,$0 \le a_i \le 2$。

思路:

 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
using i128 = __int128_t;
void solve() {
    ll n, s;
    cin >> n >> s;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    ll tot0 = 0, tot1 = 0, tot2 = 0;
    rep(i, 0, n - 1) {
        if (a[i] == 0)
            tot0++;
        else if (a[i] == 1)
            tot1++;
        else
            tot2++;
    }
    ll tot = tot1 + tot2 * 2;
    if (tot > s) {
        rep(i, 0, n - 1) cout << a[i] << ' ';
        cout << endl;
        return;
    }
    if (tot == s) {
        cout << -1 << endl;
        return;
    }
    for (ll i = 0; i <= s - tot; i += 2) {
        if ((s - tot - i) % 3 == 0) {
            cout << -1 << endl;
            return;
        }
    }
    rep(i, 0, tot0 - 1) cout << 0 << ' ';
    rep(i, 0, tot2 - 1) cout << 2 << ' ';
    rep(i, 0, tot1 - 1) cout << 1 << ' ';
    cout << endl;
    return;
}

C

题目大意:给定一组区间对 $S = \{(a_1, b_1), (a_2, b_2), \ldots, (a_m, b_m)\}$,其中对于所有 $1 \le i \le m$,都有 $a_i < b_i$,我们定义 $f(S)$ 和 $g(S)$ 如下: - 将每个 $(a_i, b_i)$ 视为数轴上的一个区间,$f(S)$ 表示这些区间的并的长度。形式化地说,$f(S)$ 是满足存在某个 $i$($1 \leq i \leq m$)使得 $[x, x+1] \subseteq [a_i, b_i]$ 的整数 $x$ 的个数。- 将每个 $(a_i, b_i)$ 视为图中的一条无向边,$g(S)$ 表示在至少包含 $3$ 条边的简单环上的点的个数。

数据范围:$1 \le t \le 10^4$,$1 \le n \le 3 \cdot 10^3$,$1 \le a_i < b_i \le 2n$,$\sum n^2 \le 9 \cdot 10^6$。

思路:

 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
using i128 = __int128_t;
const ll MAXN = 6e3 + 5;
void solve() {
    ll n, x, y;
    cin >> n;
    vl res;
    vector<vector<pll>> ma(MAXN);
    rep(i, 0, n - 1) {
        cin >> x >> y;
        ma[x].emplace_back(y, i);
        ma[y].emplace_back(x, i);
    }
    vl vis(MAXN);
    auto dfs = [&](this auto&& dfs, int x, int pa) -> void {
        vis[x] = 1;
        for (auto& [y, id] : ma[x]) {
            if (vis[y] || y == pa) continue;
            res.push_back(id);
            dfs(y, x);
        }
        return;
    };
    rep(i, 1, MAXN - 1) {
        if (!vis[i]) dfs(i, -1);
    }
    cout << sz(res) << endl;
    for (auto& p : res) cout << p + 1 << ' ';
    cout << endl;
    return;
}

D

题目大意:给定一个长度为 $n$ 的排列 $p_1, p_2, \ldots, p_n$。需要按照如下方式构造一个数组 $a_1, a_2, \ldots, a_n$: - 对于每个 $1 \leq i \leq n$,可以选择 $a_i = p_i$ 或 $a_i = 2n - p_i$。需要求出数组 $a_1, a_2, \ldots, a_n$ 中最小可能的逆序对数量。

数据范围:$1 \le t \le 10^3$,$2 \le n \le 5 \cdot 10^3$,$1 \le p_i \le n$,$\sum n \le 5 \cdot 10^3$。

思路:

 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
85
86
87
88
89
using i128 = __int128_t;
template <typename T = long long>
class Tree {
    vector<T> tree;

public:
    // 构造函数:初始化大小为 n 的树状数组,初始所有元素值为 val(外部表现为 0-based)
    Tree(int n, T val = 0) : tree(n + 1) {
        for (int i = 1; i <= n; i++) {
            tree[i] += val;
            int nxt = i + (i & -i);
            if (nxt <= n) {
                tree[nxt] += tree[i];
            }
        }
    }

    // 构造函数:使用给定的 vector 在 O(N) 时间内快速初始化建树
    Tree(const vector<T>& data) {
        int n = data.size();
        tree.resize(n + 1);
        for (int i = 1; i <= n; i++) {
            tree[i] += data[i - 1];  // data是 0-based
            int nxt = i + (i & -i);
            if (nxt <= n) {
                tree[nxt] += tree[i];
            }
        }
    }

    // 单点修改:将 0-based 下标 i 处的元素增加 val
    void add(int i, T val = 1) {
        for (++i; i < tree.size(); i += i & (-i)) {
            tree[i] += val;
        }
    }

    // 前缀求和:计算 0-based 下标区间 [0, i] 内的所有元素之和
    T pre(int i) const {
        T res = 0;
        for (++i; i > 0; i &= i - 1) {
            res += tree[i];
        }
        return res;
    }

    // 区间求和:计算 0-based 下标区间 [l, r] 内的所有元素之和
    T query(int l, int r) const {
        if (r < l) {
            return 0;
        }
        return pre(r) - pre(l - 1);  // 当 l=0 时, pre(-1) 会合理地返回 0
    }

    // 树上二分查找:返回满足前缀和 >= val 的最小 0-based 下标
    int lower_bound(T val) const {
        int w = bit_width(tree.size() - 1);
        int res = 0;
        T s = 0;
        for (int i = w - 1; i >= 0; i--) {
            int nxt = res + (1 << i);
            if (nxt < tree.size() && tree[nxt] + s < val) {
                res += (1 << i);
                s += tree[nxt];
            }
        }
        return res;  // 返回 0-based 下标:内部 1-based 下标为 res + 1,因此 0-based 为 res
    }
};
void solve() {
    ll n;
    cin >> n;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i], a[i]--;
    ll ans = 0;
    rep(i, 0, n - 1) { rep(j, i + 1, n - 1) ans += (a[i] > a[j]); }
    Tree tree1(n);
    Tree tree2(n);
    rep(i, 1, n - 1) tree2.add(a[i], 1);
    rep(i, 0, n - 1) {
        ll tem = tree1.query(a[i] + 1, n - 1);
        ll tem2 = tree2.query(a[i] + 1, n - 1);
        if (tem > tem2) ans -= (tem - tem2);
        tree1.add(a[i], 1);
        if (i != n - 1) tree2.add(a[i + 1], -1);
    }
    cout << ans << endl;
    return;
}

E1

题目大意:这是一个交互题。这是该题目的简单版本。唯一的区别在于查询次数的限制。只有在所有版本的题目都被解决后,你才能进行 Hack。有一个长度为 $n$ 的隐藏括号序列 $s$,其中 $s$ 只包含 $\texttt{'('}$ 和 $\texttt{')'}$。保证 $s$ 至少包含一个 $\texttt{'('}$ 和一个 $\texttt{')'}$。为了找出这个括号序列,你可以进行若干次查询。每次查询的形式如下:你选择一个整数 $k$ 和任意的下标 $i_1, i_2, \ldots, i_k$($1 \le k \le 1000$,$1 \le i_1, i_2, \ldots, i_k \le n$)。注意这些下标可以相同。

数据范围:$1 \le t \le 20$。

思路:

  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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
ll ask(ll k, vl& res) {
    cout << "? " << k << ' ';
    rep(i, 0, k - 1) cout << res[i] << ' ';
    cout.flush();
    ll op;
    cin >> op;
    return op;
}
void report(string s) {
    cout << "! " << s << '\n';
    cout.flush();
}
void solve() {
    ll n;
    cin >> n;
    vector<char> res(n);
    string res2 = "";
    ll l = 1, r = n - 1, mid, ans = -1;
    auto check = [&](ll mid) -> bool {
        vl tem;
        rep(i, 0, mid) tem.push_back(i + 1);
        ll op = ask(sz(tem), tem);
        return op > 0;
    };
    while (l <= r) {
        mid = (l + r) / 2;
        if (check(mid)) {
            r = mid - 1;
            ans = mid;
        } else
            l = mid + 1;
    }
    if (ans == -1) {
        res[n - 1] = '(', res[0] = ')';
        vl re;
        rep(i, 1, n - 2) { re.push_back(i); }
        int m = sz(re);
        for (int i = 0; i < m - 1; i += 2) {
            vl tem;
            tem.push_back(n), tem.push_back(n);
            tem.push_back(re[i] + 1), tem.push_back(re[i + 1] + 1);
            tem.push_back(re[i] + 1);
            ll op = ask(sz(tem), tem);
            if (op == 0) {
                res[re[i]] = '(', res[re[i + 1]] = '(';
            } else if (op == 1) {
                res[re[i]] = '(', res[re[i + 1]] = ')';
            } else if (op == 2) {
                res[re[i]] = ')', res[re[i + 1]] = ')';
            } else {
                res[re[i]] = ')', res[re[i + 1]] = '(';
            }
        }
        if (m % 2 == 1) {
            vl tem;
            tem.push_back(n);
            tem.push_back(re[m - 1] + 1);
            ll op = ask(sz(tem), tem);
            if (op == 1)
                res[re[m - 1]] = ')';
            else
                res[re[m - 1]] = '(';
        }
        rep(i, 0, n - 1) res2.push_back(res[i]);
        report(res2);
        return ;
    }
    res[ans] = ')', res[ans - 1] = '(';
    vl re;
    rep(i, 0, n - 1) {
        if (i == ans || i == ans - 1) continue;
        re.push_back(i);
    }
    int m = sz(re);
    for (int i = 0; i < m - 1; i += 2) {
        vl tem;
        tem.push_back(ans), tem.push_back(ans);
        tem.push_back(re[i] + 1), tem.push_back(re[i + 1] + 1);
        tem.push_back(re[i] + 1);
        ll op = ask(sz(tem), tem);
        if (op == 0) {
            res[re[i]] = '(', res[re[i + 1]] = '(';
        } else if (op == 1) {
            res[re[i]] = '(', res[re[i + 1]] = ')';
        } else if (op == 2) {
            res[re[i]] = ')', res[re[i + 1]] = ')';
        } else {
            res[re[i]] = ')', res[re[i + 1]] = '(';
        }
    }
    if (m % 2 == 1) {
        vl tem;
        tem.push_back(ans);
        tem.push_back(re[m - 1] + 1);
        ll op = ask(sz(tem), tem);
        if (op == 1)
            res[re[m - 1]] = ')';
        else
            res[re[m - 1]] = '(';
    }
    rep(i, 0, n - 1) res2.push_back(res[i]);
    report(res2);
    return;
}

E2

题目大意:这是一个交互题。这是该题目的中等版本,唯一的区别在于查询次数的限制。只有在所有版本都被解决后,你才能进行 Hack。有一个长度为 $n$ 的隐藏括号序列 $s$,其中 $s$ 只包含 $\texttt{'('}$ 和 $\texttt{')'}$。保证 $s$ 至少包含一个 $\texttt{'('}$ 和一个 $\texttt{')'}$。为了找出这个括号序列,你可以进行查询。每次查询的形式如下:你选择一个整数 $k$ 和任意的下标 $i_1, i_2, \ldots, i_k$($1 \le k \le 1000$,$1 \le i_1, i_2, \ldots, i_k \le n$)。注意这些下标可以相同。

数据范围:$1 \le t \le 20$。

思路:

 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
ll ask(ll k, vl& res) {
    cout << "? " << k << ' ';
    rep(i, 0, k - 1) cout << res[i] << ' ';
    cout.flush();
    ll op;
    cin >> op;
    return op;
}
void report(string s) {
    cout << "! " << s << '\n';
    cout.flush();
}
void solve() {
    ll n;
    cin >> n;
    vector<char> res(n);
    string res2 = "";
    ll l = 1, r = n - 1, mid, ans = -1;
    auto check = [&](ll mid) -> bool {
        vl tem;
        rep(i, 0, mid) tem.push_back(i + 1);
        ll op = ask(sz(tem), tem);
        return op > 0;
    };
    while (l <= r) {
        mid = (l + r) / 2;
        if (check(mid)) {
            r = mid - 1;
            ans = mid;
        } else
            l = mid + 1;
    }

    ll idx1 = (ans == -1 ? 0 : ans), idx2 = (ans == -1 ? n - 1 : ans - 1);
    res[idx1] = ')', res[idx2] = '(';
    vl re;
    rep(i, 0, n - 1) {
        if (i == idx1 || i == idx2) continue;
        re.push_back(i + 1);
    }
    int m = sz(re);
    vl cnt = {2, 3, 4, 6, 7, 9, 10, 12, 18, 25, 35, 50};
    vl add = {2, 9, 20, 54, 77, 135, 170, 252, 594, 1175, 2345, 5000};
    ll tem = 574;
    map<ll, ll> ma;
    rep(i, 0, (1LL << 12) - 1) {
        ll tem2 = tem;
        rep(j, 0, 11) {
            if (i >> j & 1) tem2 += add[j];
        }
        ma[tem2] = i;
    }
    for (int i = 0; i <= m - 1; i += 12) {
        vl tem2(12, idx2 + 1);
        rep(j, 0, 11) {
            if (i + j <= m - 1) tem2[j] = re[i + j];
        }
        vl tem3;
        rep(j, 0, 11) {
            rep(v, 1, cnt[j]) {
                tem3.push_back(idx2 + 1);
                tem3.push_back(idx1 + 1);
                tem3.push_back(idx2 + 1);
                tem3.push_back(tem2[j]);
            }
            if (j != 11) {
                rep(v, 1, 2 * cnt[j] + 1) { tem3.push_back(idx1 + 1); }
            }
        }
        ll op = ask(sz(tem3), tem3);
        rep(j, 0, 11) {
            if (i + j >= m) continue;
            if (ma[op] >> j & 1)
                res[re[i + j] - 1] = ')';
            else
                res[re[i + j] - 1] = '(';
        }
    }
    rep(i, 0, n - 1) res2.push_back(res[i]);
    report(res2);
    return;
}

E3

题目大意:这是一个交互题。这是该题的困难版本。唯一的区别在于查询次数的限制。只有在所有版本都被解决后,才能进行 Hack。有一个长度为 $n$ 的隐藏括号序列 $s$,其中 $s$ 只包含 $\texttt{'('}$ 和 $\texttt{')'}$。保证 $s$ 至少包含一个 $\texttt{'('}$ 和一个 $\texttt{')'}$。你可以通过询问来找出这个括号序列。

数据范围:$1 \le t \le 20$。

思路:

 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
ll ask(ll k, vl& res) {
    cout << "? " << k << ' ';
    rep(i, 0, k - 1) cout << res[i] << ' ';
    cout.flush();
    ll op;
    cin >> op;
    return op;
}
void report(string s) {
    cout << "! " << s << '\n';
    cout.flush();
}
void solve() {
    ll n;
    cin >> n;
    vector<char> res(n);
    string res2 = "";
    ll l = 1, r = n - 1, mid, ans = -1;
    auto check = [&](ll mid) -> bool {
        vl tem;
        rep(i, 0, mid) tem.push_back(i + 1);
        ll op = ask(sz(tem), tem);
        return op > 0;
    };
    while (l <= r) {
        mid = (l + r) / 2;
        if (check(mid)) {
            r = mid - 1;
            ans = mid;
        } else
            l = mid + 1;
    }

    ll idx1 = (ans == -1 ? 0 : ans), idx2 = (ans == -1 ? n - 1 : ans - 1);
    res[idx1] = ')', res[idx2] = '(';
    vl re;
    rep(i, 0, n - 1) {
        if (i == idx1 || i == idx2) continue;
        re.push_back(i + 1);
    }
    int m = sz(re);
    vl cnt = {2, 3, 4, 6, 7, 9, 10, 12, 18, 25, 35, 50};
    vl add = {2, 9, 20, 54, 77, 135, 170, 252, 594, 1175, 2345, 5000};
    ll tem = 574;
    map<ll, ll> ma;
    rep(i, 0, (1LL << 12) - 1) {
        ll tem2 = tem;
        rep(j, 0, 11) {
            if (i >> j & 1) tem2 += add[j];
        }
        ma[tem2] = i;
    }
    for (int i = 0; i <= m - 1; i += 12) {
        vl tem2(12, idx2 + 1);
        rep(j, 0, 11) {
            if (i + j <= m - 1) tem2[j] = re[i + j];
        }
        vl tem3;
        rep(j, 0, 11) {
            rep(v, 1, cnt[j]) {
                tem3.push_back(idx2 + 1);
                tem3.push_back(idx1 + 1);
                tem3.push_back(idx2 + 1);
                tem3.push_back(tem2[j]);
            }
            if (j != 11) {
                rep(v, 1, 2 * cnt[j] + 1) { tem3.push_back(idx1 + 1); }
            }
        }
        ll op = ask(sz(tem3), tem3);
        rep(j, 0, 11) {
            if (i + j >= m) continue;
            if (ma[op] >> j & 1)
                res[re[i + j] - 1] = ')';
            else
                res[re[i + j] - 1] = '(';
        }
    }
    rep(i, 0, n - 1) res2.push_back(res[i]);
    report(res2);
    return;
}