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

Codeforces Round #1042(Div.3)

D

题目大意:Kagari 正准备对一棵树进行归档,她知道归档的成本取决于树的直径 ¹。为了降低成本,她的目标是首先尽可能缩小直径。她可以对树执行以下操作: - 选择两个顶点 $s$ 和 $t$。设从 $s$ 到 $t$ 的简单路径 ² 上的顶点序列为 $v_{0},v_{1},…,v_{k}$,其中 $v_{0} = s,v_{k} = t$。移除路径上的所有边。即移除边 $(v_{0},v_{1}),(v_{1},v_{2}),…,(v_{k-1},v_{k})$。- 将顶点 $v_{1},v_{2},…,v_{k}$ 直接连接到 $v_{0}$。即添加边 $(v_{0},v_{1}),(v_{0},v_{2}),…,(v_{0},v_{k})$。

数据范围:$\sum n \le 2⋅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
using i128 = __int128_t;
void solve() {
    ll n, x, y;
    cin >> n;
    vvl ma(n);
    rep(i, 1, n - 1) {
        cin >> x >> y;
        ma[x - 1].push_back(y - 1);
        ma[y - 1].push_back(x - 1);
    }
    if (n == 2) {
        cout << 0 << endl;
        return;
    }
    ll ans = 0;
    rep(i, 0, n - 1) {
        if (sz(ma[i]) == 1) ans++;
    }
    ll ans2 = LLONG_MAX;
    rep(i, 0, n - 1) {
        ll tem = 0;
        for (auto& p : ma[i]) {
            tem += (sz(ma[p]) == 1);
        }
        ans2 = min(ans2, ans - tem);
    }
    cout << ans2 << endl;
    return;
}

E

题目大意:你有一个长度为 $n$ 的数组 $a$,对于每个满足 $1 \le i < n$ 的索引 $i$,你只能执行以下操作最多一次: - 令 $a_i := a_i \oplus a_{i+1}$,其中 $\oplus$ 表示按位异或运算。你可以按任意顺序选择这些索引并执行操作。给定另一个数组 $b$,判断 $a$ 能否通过这些操作转换为 $b$。

数据范围:$1 \le t \le 10^4$,$1 \le n \le 2 \cdot 10^5$,$0 \le a_i < 2^{30}$,$0 \le b_i < 2^{30}$,$\sum n \le 2 \cdot 10^5$。

思路:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
using i128 = __int128_t;
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];
    if (a[n - 1] != b[n - 1]) {
        cout << "NO" << endl;
        return;
    }
    frep(i, n - 2, 0) {
        if (a[i] == b[i] || ((a[i] ^ a[i + 1]) == b[i]) || ((a[i] ^ b[i + 1]) == b[i])) continue;
        cout << "NO" << endl;
        return;
    }
    cout << "YES" << endl;
    return;
}

F

题目大意:Yuri 有两个长度为 $n$ 的 01 字符串 $a$ 和 $b$,这两个字符串可以定义一个 $n \times n$ 的网格。记 $(i, j)$ 为第 $i$ 行第 $j$ 列的单元格。单元格 $(i, j)$ 的值为 $a_i \oplus b_j$,其中 $\oplus$ 表示按位异或运算。Yuri 的旅途永远从单元格 $(1, 1)$ 开始。从单元格 $(i, j)$ 出发,她只能向下移动到单元格 $(i+1, j)$ 或者向右移动到单元格 $(i, j+1)$。当且仅当路径上所有单元格——包括起始单元格 $(1, 1)$——的值均为 $0$ 时,她才能沿着这条路径移动。在她出发之前,她可以进行任意次下列操作。

数据范围:$1 \le t \le 10^4$,$1 \le n \le 2 \cdot 10^5$,$\sum n \le 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
using i128 = __int128_t;
void solve() {
    ll n;
    cin >> n;
    string s, t;
    cin >> s >> t;
    vl pre(n);
    pre[0] = (t[0] == '0' ? -1 : 1);
    rep(i, 1, n - 1) { pre[i] = pre[i - 1] + (t[i] == '0' ? -1 : 1); }
    ranges::sort(pre);
    vl pre2(n);
    pre2[0] = pre[0];
    rep(i, 1, n - 1) pre2[i] = pre2[i - 1] + pre[i];
    ll ans = 0;
    ll tem = 0;
    rep(i, 0, n - 1) {
        tem += (s[i] == '0' ? 1 : -1);
        ll tem2 = ranges::lower_bound(pre, tem) - pre.begin();
        ans += tem * tem2 - (tem2 > 0 ? pre2[tem2 - 1] : 0);
        ans += (tem2 > 0 ? pre2[n - 1] - pre2[tem2 - 1] : pre2[n - 1]) - (n - tem2) * tem;
    }
    cout << (n * n * (n + 1) - ans) / 2 << endl;
    return;
}

G

题目大意:为了帮助 Kudryavka 提高数学能力,她得到了一个包含 $n$ 个互不相同正整数的集合 $S$。初始时,她的得分为 $1$。只要集合不为空,她可以对集合执行任意次数如下操作: 1. 设 $S$ 的最小值为 $m$。2. 将她的得分乘以 $m$。3. 从 $S$ 中移除 $m$。4. 对于每个满足 $1 \le i < m$ 的整数 $i$,将 $i$ 加入集合 $S$。可以证明在此步骤中不会添加重复元素。她沉迷于执行这些操作,但在进行了 $k$ 次操作后,她忘记了自己的得分。需要帮她计算她的得分,结果对 $10^9+7$ 取模。

数据范围:$1 \le t \le 10^4$,$1 \le n \le 2 \times 10^5$,$1 \le k \le 10^9$,$1 \le s_i \le 10^9$,$\sum n \le 2 \times 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
using i128 = __int128_t;
constexpr int MOD = 1e9 + 7;
constexpr int MX = 1e5 + 1;
ll F[MX];      // 预处理阶乘
ll INV_F[MX];  // 预处理逆元
ll mul(ll x, ll y) { return x * y % MOD; }
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; }
ll l[31];
ll val[31];
auto init2 = [] {
    l[0] = 0, val[0] = 1;
    rep(i, 1, 30) {
        l[i] = l[i - 1] * 2 + 1;
        val[i] = mul(mul(val[i - 1], val[i - 1]), i);
    }
    return 0;
}();
void solve() {
    ll n, k;
    cin >> n >> k;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    ranges::sort(a);
    ll ans = 1;
    rep(i, 0, n - 1) {
        if (k == 0) break;
        ll tem = (a[i] > 31 ? LLONG_MAX : (1LL << (a[i] - 1)));
        if (tem <= k) {
            ans = mul(ans, a[i]);
            ans = mul(ans, val[a[i] - 1]);
            k -= tem;
        } else {
            ans = mul(ans, a[i]);
            k--;
            ll tem2 = (a[i] - 1 >= 31 ? 30 : a[i] - 1);
            while (k > 0 && tem2 > 0) {
                if (k <= l[tem2 - 1]) {
                    tem2--;
                    continue;
                }
                ans = mul(ans, val[tem2 - 1]);
                k -= l[tem2 - 1];
                if (k == 0) break;
                ans = mul(ans, tem2);
                k--;
                if (k == 0) break;
                tem2--;
            }
        }
    }
    cout << ans << endl;
    return;
}

H

题目大意:羽未有一个数组 $a$,包含了 $n$ 个介于 $1$ 和 $m$ 之间的整数。她非常喜欢互质的数,想从找出四个不同的索引 $p,q,r,s$($1 \le p,q,r,s \le n$)使得 $\gcd(a_p,a_q)=1$ 并且 $\gcd(a_r,a_s)=1$,其中 $\gcd(x,y)$ 表示 $x$ 和 $y$ 的最大公因数。如果有多个答案,你可以输出其中任意一组。

数据范围:$1 \le t \le 10^4$,$4 \le n \le 2 \cdot 10^5$,$1 \le m \le 10^6$,$1 \le a_i \le m$,$\sum n \le 2 \cdot 10^5$,$\sum m \le 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
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
using i128 = __int128_t;
constexpr int MX = 1e6 + 5;
vector<int> divisors[MX];
vector<int> fac[MX];
auto init = [] {
    rep(i, 1, MX - 1) {
        for (int j = i; j < MX; j += i) {
            divisors[j].push_back(i);
        }
    }
    rep(i, 2, MX - 1) {
        if (fac[i].empty()) {
            for (int j = i; j < MX; j += i) {
                fac[j].push_back(i);
            }
        }
    }
    return 0;
}();
void solve() {
    ll n, m;
    cin >> n >> m;
    vl a(n);
    rep(i, 0, n - 1) cin >> a[i];
    ll maxx = *max_element(all(a));
    vl cnt(maxx + 1);
    vl cnt2(maxx + 1);
    rep(i, 0, n - 1) {
        for (auto& p : divisors[a[i]]) {
            cnt[p]++;
        }
    }
    rep(i, 1, maxx) {
        if (!cnt[i]) continue;
        int tem = sz(fac[i]);
        ll tot = 0;
        rep(j, 1, (1 << tem) - 1) {
            ll tem2 = 1;
            rep(v, 0, tem - 1) {
                if (j >> v & 1) {
                    tem2 *= fac[i][v];
                }
            }
            if (popcount((unsigned)j) % 2 == 1)
                tot += cnt[tem2];
            else
                tot -= cnt[tem2];
        }
        cnt2[i] = tot;
    }
    vl deg(n);
    rep(i, 0, n - 1) { deg[i] = n - cnt2[a[i]] - (a[i] == 1); }
    int idx1 = -1, idx2 = -1;
    rep(i, 0, n - 1) {
        if (!deg[i]) continue;
        rep(j, 0, n - 1) {
            if (i == j) continue;
            if (__gcd(a[i], a[j]) == 1) {
                idx1 = i, idx2 = j;
                break;
            }
        }
        if (idx1 != -1) break;
    }
    if (idx1 == -1) {
        cout << 0 << endl;
        return;
    }
    rep(i, 0, n - 1) {
        if (i == idx1 || i == idx2) continue;
        ll tem = deg[i];
        if (__gcd(a[i], a[idx1]) == 1) tem--;
        if (__gcd(a[i], a[idx2]) == 1) tem--;
        if (tem == 0) continue;
        rep(j, 0, n - 1) {
            if (j == i || j == idx1 || j == idx2) continue;
            if (__gcd(a[i], a[j]) == 1) {
                cout << i + 1 << ' ' << j + 1 << ' ' << idx1 + 1 << ' ' << idx2 + 1 << endl;
                return;
            }
        }
    }
    vl idx3, idx4;
    rep(i, 0, n - 1) {
        if (i == idx1 || i == idx2) continue;
        if (__gcd(a[i], a[idx1]) == 1 && sz(idx3) < 2) idx3.push_back(i);
        if (__gcd(a[i], a[idx2]) == 1 && sz(idx4) < 2) idx4.push_back(i);
    }
    for (auto& p : idx3) {
        for (auto& q : idx4) {
            if (p != q) {
                cout << p + 1 << ' ' << idx1 + 1 << ' ' << q + 1 << ' ' << idx2 + 1 << endl;
                return;
            }
        }
    }
    cout << 0 << endl;
    return;
}