Featured image of post Codeforces Round #1065(Div.3)

Codeforces Round #1065(Div.3)

D

题目大意:给定长度为 $n$ 的排列 $p$ ,判断是否存在一棵 $n$ 点标号树,使得任意边 $(u,v)$ 在 $u

数据范围:$1 \leq t \leq 10^4, 2 \leq n \leq 2 \cdot 10^5, 1 \leq p_i \leq n, \sum n \leq 2 \cdot 10^5$

思路:

 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
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);
    vl pos(n);
    rep(i, 0, n - 1) {
        cin >> a[i];
        a[i]--;
    }
    vl pre(n);
    pre[0] = a[0];
    rep(i, 1, n - 1) pre[i] = min(pre[i - 1], a[i]);
    vl suf(n);
    suf[n - 1] = a[n - 1];
    frep(i, n - 2, 0) suf[i] = max(suf[i + 1], a[i]);
    rep(i, 0, n - 2) {
        if (pre[i] > suf[i + 1]) {
            cout << "No" << endl;
            return;
        }
    }
    cout << "Yes" << endl;
    return;
}

E

题目大意:构造一个长度为 $n$ 的排列 $p$ ,使得连续三项两两互质的位置数量不超过 $6$ 。只需要输出任意合法排列。

数据范围:$1 \leq t \leq 10^4, 3 \leq n \leq 2 \cdot 10^5, \sum n \leq 2 \cdot 10^5$

思路:

 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
void solve() {
    ll n;
    cin >> n;
    vl even;
    vl odd;
    vl tem;
    rep(i, 1, n) {
        if (i % 2 == 0)
            even.push_back(i);
        else if (i % 3 == 0)
            odd.push_back(i);
        else
            tem.push_back(i);
    }
    while (!tem.empty() && sz(even) >= 2) {
        cout << tem.back() << ' ';
        tem.pop_back();
        cout << even.back() << ' ';
        even.pop_back();
        cout << even.back() << ' ';
        even.pop_back();
    }
    while (!tem.empty() && sz(odd) >= 2) {
        cout << tem.back() << ' ';
        tem.pop_back();
        cout << odd.back() << ' ';
        odd.pop_back();
        cout << odd.back() << ' ';
        odd.pop_back();
    }
    while (!even.empty()) {
        cout << even.back() << ' ';
        even.pop_back();
    }
    while (!odd.empty()) {
        cout << odd.back() << ' ';
        odd.pop_back();
    }
    while (!tem.empty()) {
        cout << tem.back() << ' ';
        tem.pop_back();
    }
    cout << endl;
    return;
}

F

题目大意:给定长度为 $n$ 的排列 $p$ ,判断是否存在一棵 $n$ 点标号树,使得任意边 $(u,v)$ 在 $u

数据范围:$1 \leq t \leq 10^4, 2 \leq n \leq 2 \cdot 10^5, 1 \leq p_i \leq n, \sum n \leq 2 \cdot 10^5$

思路:

 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
void solve() {
    ll n;
    cin >> n;
    vl a(n);
    vl pos(n);
    rep(i, 0, n - 1) { cin >> a[i]; }
    vl pre(n);
    pre[0] = a[0];
    rep(i, 1, n - 1) pre[i] = min(pre[i - 1], a[i]);
    vl suf(n);
    vl idx(n);
    suf[n - 1] = a[n - 1];
    idx[n - 1] = n - 1;
    frep(i, n - 2, 0) {
        suf[i] = suf[i + 1];
        idx[i] = idx[i + 1];
        if (a[i] > suf[i + 1]) {
            suf[i] = a[i];
            idx[i] = i;
        }
    }
    vector<pll> res;
    rep(i, 0, n - 2) {
        if (pre[i] > suf[i + 1]) {
            cout << "No" << endl;
            return;
        }
    }
    cout << "Yes" << endl;
    int l = 0;
    while (l < n) {
        rep(i, l, idx[l] - 1) { res.emplace_back(a[i], a[idx[l]]); }
        if (l > 0) res.emplace_back(a[idx[l]], pre[l - 1]);
        l = idx[l] + 1;
    }
    for (auto& p : res) cout << p.first << ' ' << p.second << endl;
    return;
}

G

题目大意:给定两个长度为 $n$ 的数组 $a,b$ ,其中 $a_i \leq b_i$ 。每次操作可以把某个 $a_i$ 加一,或者把整个数组 $a$ 全部乘二。设最少需要 $x$ 次操作把 $a$ 变成 $b$ ,求 $x$ 以及恰好 $x$ 次操作的不同序列数。

数据范围:$1 \leq t \leq 10^4, 2 \leq n \leq 2 \cdot 10^5, 1 \leq a_i \leq b_i \leq 10^6, \sum n \leq 2 \cdot 10^5, \operatorname{mod}=10^6+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
const ll MOD = 1e6 + 3;
constexpr int MX = 1e6 + 3;
ll F[MX];      // 预处理阶乘
ll INV_F[MX];  // 预处理逆元
ll qpow(ll x, int n) {
    ll res = 1;
    for (; n; n >>= 1) {
        if (n % 2) res = res * x % MOD;
        x = x * x % MOD;
    }
    return res;
}
auto init = [] {
    F[0] = 1;
    for (int i = 1; i < MX; i++) F[i] = F[i - 1] * i % MOD;  // 预处理阶乘
    INV_F[MX - 1] = qpow(F[MX - 1], MOD - 2);
    for (int i = MX - 1; i; i--) {
        INV_F[i - 1] = INV_F[i] * i % MOD;
    }  // 预处理逆元
    return 0;
}();
// 计算C(n,m),即从n个数中取m个数
ll comb(int n, int m) { return m < 0 || m > n ? 0 : F[n] * INV_F[m] % MOD * INV_F[n - m] % MOD; }
void solve() {
    ll n;
    cin >> n;
    vl a(n), b(n);
    rep(i, 0, n - 1) cin >> a[i];
    rep(i, 0, n - 1) cin >> b[i];
    ll tem = LLONG_MAX;
    rep(i, 0, n - 1) { tem = min(tem, 1LL * (int)log2(b[i] / a[i])); }
    ll ans = tem;
    rep(i, 0, n - 1) {
        ans += b[i] / (1LL << tem) - a[i];
        ans += popcount((unsigned)(b[i] % (1LL << tem)));
    }
    cout << ans << ' ';
    vl cnt(tem + 1);
    rep(i, 0, n - 1) {
        ll tem2 = b[i] / (1LL << tem) - a[i];
        cnt[0] += tem2;
        rep(j, 1, tem) { cnt[j] += ((b[i] % (1LL << tem)) >> (j - 1) & 1); }
    }
    ll res = 1;
    if (cnt[0] >= MOD) {
        cout << 0 << endl;
        return;
    }
    res = F[cnt[0]];
    rep(i, 1, tem) { res = (res * F[cnt[i]] % MOD); }
    rep(i, 0, n - 1) { res = res * INV_F[b[i] / (1LL << tem) - a[i]] % MOD; }
    cout << res << endl;
    return;
}

H

题目大意:定义 $v(b,x)$ 为满足 $b^k \mid x$ 的最大 $k$ 。给定 $n,m$ ,在所有长度为 $n$ 且严格递增、元素位于 $[1,m]$ 的数组 $a$ 中,最大化 $\sum_{i=2}^{n}v(i,a_i)$ 。

数据范围:$1 \leq t \leq 10^4, 2 \leq n \leq m \leq 2 \cdot 10^5, \sum m \leq 2 \cdot 10^5$

思路:

  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
105
106
107
108
109
110
111
112
struct Info {
    ll sum;
    Info(ll x = 0) : sum(x) {}
};
Info operator+(const Info& a, const Info& b) {
    Info c;
    c.sum = max(a.sum, b.sum);
    return c;
}
template <typename T>
class SegmentTree {
    int n;
    vector<T> tree;
    T merge_val(T a, T b) const { return a + b; }  // 合并子树

    void maintain(int node) {  // 维护整棵树
        tree[node] = merge_val(tree[node * 2], tree[node * 2 + 1]);
    }

    void build(const vector<T>& a, int node, int l, int r) {
        if (l == r) {
            tree[node] = a[l];
            return;
        }
        int m = (l + r) / 2;
        build(a, node * 2, l, m);
        build(a, node * 2 + 1, m + 1, r);
        maintain(node);
    }  // 建树

    void update(int node, int l, int r, int i, T val) {
        if (l == r) {
            tree[node] = val;
            return;
        }
        int m = (l + r) / 2;
        if (i <= m)
            update(node * 2, l, m, i, val);
        else
            update(node * 2 + 1, m + 1, r, i, val);
        maintain(node);
    }  // 更新i处的值为val

    T query(int node, int l, int r, int ql, int qr) const {
        if (ql <= l && r <= qr) return tree[node];
        int m = (l + r) / 2;
        if (qr <= m) return query(node * 2, l, m, ql, qr);
        if (ql > m) return query(node * 2 + 1, m + 1, r, ql, qr);
        T l_res = query(node * 2, l, m, ql, qr);
        T r_res = query(node * 2 + 1, m + 1, r, ql, qr);
        return merge_val(l_res, r_res);
    }  // 查询[ql,qr]的值

    int find_first(int node, int l, int r, int ql, int qr, T val) const {
        if (r < ql || l > qr) return -1;
        if (tree[node].val < val) return -1;
        if (l == r) return l;
        int m = (l + r) >> 1;
        int res = find_first(node << 1, l, m, ql, qr, val);
        if (res != -1) return res;
        return find_first(node << 1 | 1, m + 1, r, ql, qr, val);
    }
    // 若固定左端点,需要记录前缀分段最大值,并加被待求区间完全覆盖的剪枝

    int find_last(int node, int l, int r, int ql, int qr, T val) const {
        if (r < ql || l > qr) return -1;
        if (tree[node].val < val) return -1;
        if (l == r) return l;
        int m = (l + r) >> 1;
        int res = find_last(node << 1 | 1, m + 1, r, ql, qr, val);
        if (res != -1) return res;
        return find_last(node << 1, l, m, ql, qr, val);
    }

public:
    SegmentTree(int n, T init_val) : SegmentTree(vector<T>(n, init_val)) {}

    // 传入一个数组维护
    SegmentTree(const vector<T>& a) : n(a.size()), tree(2 << bit_width(a.size() - 1)) { build(a, 1, 0, n - 1); }

    void update(int i, T val) { update(1, 0, n - 1, i, val); }  // 更新i的值为val

    T query(int ql, int qr) const { return query(1, 0, n - 1, ql, qr); }  // 查询[ql,qr]的值

    T get(int i) const { return query(1, 0, n - 1, i, i); }  // 取出i处的值

    // 查询[ql,qr]中第一个满足条件的下标
    int find_first(int ql, int qr, T val) const { return find_first(1, 0, n - 1, ql, qr, val); }

    // 查询[ql,qr]中最后一个满足条件的下标
    int find_last(int ql, int qr, T val) const { return find_last(1, 0, n - 1, ql, qr, val); }
};
void solve() {
    ll n, m;
    cin >> n >> m;
    vector<Info> init(m - n + 1, Info(0));
    SegmentTree<Info> seg(init);
    rep(i, 2, n) {
        for (ll j = (i + m - n) / i * i; j >= i; j -= i) {
            ll tem = seg.query(0, j - i).sum;
            ll tem2 = j;
            ll tem3 = 0;
            while (tem2 % i == 0) {
                tem3++;
                tem2 /= i;
            }
            if (tem3 + tem > seg.get(j - i).sum) seg.update(j - i, Info(tem + tem3));
        }
    }
    cout << seg.query(0, m - n).sum << endl;
    return;
}