Codeforces Global Round 31 (Div. 1 + Div. 2) A ~ E

传奇构造场。

A. Carnival Wheel

time limit per test: 1 second

memory limit per test: 256 megabytes

You have a prize wheel divided into l l l sections, numbered from 0 0 0 to l − 1 l-1 l−1. The sections are arranged in a circle, so after section l − 1 l-1 l−1, the numbering continues again from section 0 0 0.

Initially, the prize pointer is at section a a a. Each time you spin the wheel, the pointer moves exactly b b b sections forward. That is, after one spin, the pointer moves from section a a a to section ( a + b )   m o d   l (a+b)\bmod l (a+b)modl, after two spins to ( a + 2 b )   m o d   l (a+2b)\bmod l (a+2b)modl, and so on ∗ ^{\text{∗}} ∗.

You may spin the wheel any number of times (including zero). After you stop, the section where the pointer finally lands determines your prize: you receive an amount equal to the number of that section.

What is the maximum prize you can obtain?

∗ ^{\text{∗}} ∗Here, x   m o d   y x \bmod y xmody denotes the remainder from dividing x x x by y y y.

经典gcd

cpp 复制代码
void solve()
{
    int l, a, b;
    cin >> l >> a >> b;
    int g = gcd(l, b);

    if (g == 1) {
        cout << l - 1 << endl;
        return;
    } else {
        int ans = a;
        for (int i = 1; i <= l; i++) {
            ans = max((i * b + a) % l, ans);
        }
        cout << ans << endl;
        return;
    }
}

B. Ashmal

time limit per test: 1 second

memory limit per test: 256 megabytes

You have an array a a a of n n n strings a 1 , a 2 , ... , a n a_{1}, a_{2}, \ldots, a_{n} a1,a2,...,an, each consisting of lowercase English letters, and an empty string s s s.

In the i i i-th ( 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n) step, you should do one of the following:

  • add a i a_{i} ai to the beginning of s s s, or
  • add a i a_{i} ai to the end of s s s.

For example, if before the i i i-th step s = a b a s = \mathtt{aba} s=aba and a i = b b a a_{i} = \mathtt{bba} ai=bba, after the i i i-th step, s s s will be equal to a b a b b a \mathtt{ababba} ababba or b b a a b a \mathtt{bbaaba} bbaaba.

What's the lexicographically smallest string s s s you can reach after n n n steps?

A string a a a is lexicographically smaller than a string b b b of the same length, if and only if the following holds:

  • in the first position where a a a and b b b differ, the string a a a has a letter that appears earlier in the alphabet than the corresponding letter in b b b.

简单贪心

cpp 复制代码
void solve()
{
    int n;
    cin >> n;
    vector<string> ss(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> ss[i];
    } 

    string ans ;
    for (int i = 1; i <= n; i++) {
        string ans1 = ans + ss[i], ans2 = ss[i] + ans;
        ans = min(ans1, ans2);
    }

    cout << ans << endl;
}

C. XOR-factorization

time limit per test: 2 seconds

memory limit per test: 256 megabytes

Ostad thinks that the usual way of factoring numbers is too mathematical, so he invented a new notion called XOR-factorization, which is more computer-science-like. For a given integer n n n, a sequence of integers a 1 , a 2 , ... , a k a_1, a_2, \ldots, a_k a1,a2,...,ak with 0 ≤ a i ≤ n 0 \le a_i \le n 0≤ai≤n for all i i i is called a XOR-factorization of n n n if and only if

a 1 ⊕ a 2 ⊕ ⋯ ⊕ a k = n , a_1 \oplus a_2 \oplus \cdots \oplus a_k = n, a1⊕a2⊕⋯⊕ak=n,

where ⊕ \oplus ⊕ denotes the bitwise XOR operation.

You are given integers n n n and k k k. Find a XOR-factorization a 1 , a 2 , ... , a k a_1, a_2, \ldots, a_k a1,a2,...,ak of n n n that maximizes the sum a 1 + a 2 + ⋯ + a k a_1 + a_2 + \cdots + a_k a1+a2+⋯+ak.

It can be proven that under the problem conditions, a XOR-factorization always exists.

传奇构造题。

这里官方题解中提到了一个很经典的思考方式,常用于数位DP ,就是松 / 紧的思想,简单来说:

  • 对于一个二进制数字 x x x 如: ( 1011000 ) 2 (1011000)_2 (1011000)2
  • 对于目前由高到低枚举到的第 b 位,如果 b 位为 1 (x >> bit & 1),则如果这一位置我取 0,则后面可以随便取 1,大小也不会更大,这就是松,反之就是紧。

对于这道题,分奇偶讨论:

  • 奇数情况很简单,直接输出 k k k 个 n n n 即可,这种情况一定是和最大的。
  • 偶数情况就很复杂了:
    • 首先要想到,对于数字 n n n 的第 bit 位,如果这位是 1 1 1,则最后 k k k 个数字中必定会有奇数 个数字在这一位置取 1 1 1;如果这位是 0 0 0,则最后 k k k 个数字中必定会有偶数 个数字在这一位置取 1 1 1;
    • 所以,我们想要让取到的 1 1 1 尽可能多,且 k k k 个数字中的每个数字都满足小于 n n n 的条件。
    • 对于当前二进制位是 1 1 1 的,我们必定会只选择 k − 1 k - 1 k−1 个数字这一位构造为 1 1 1,则剩下的这个数字就满足我们一开始提到的 的情况了,则对于这个 的数字,我就可以选择在它 后面 (较低位)的 0 的位置都填上 1 1 1 (当然得满足最后当前位置为 1 的总个数为偶数)。

所以就有如下代码:

cpp 复制代码
void solve()
{
    int n, k;
    cin >> n >> k;

    if (k & 1) {
        for (int i = 1; i <= k; i++) {
            cout << n << ' ';
        }
        cout << endl;
    } else {
        vector<int> ans(k + 1);
        int num = 0;  // 松的数字的个数
        for (int bit = __lg(n); bit >= 0; bit--) {
            if (n >> bit & 1) {
                for (int i = 1; i <= k; i++) {
                    if (i != min(num + 1, k)) {
                        ans[i] |= 1LL << bit;
                    }
                }
                if (num < k) num++;
            } else {
                for (int i = 1; i <= num >> 1 << 1; i++) {  // 必须是偶数个 1 
                    ans[i] |= 1LL << bit;
                } 
            }
        }

        for (int i = 1; i <= k; i++) {
            cout << ans[i] << ' ';
        }
        cout << endl;
    }
}

D. Insolvable Disks

time limit per test: 2 seconds

memory limit per test: 256 megabytes

You are given n n n different integer points x 1 < x 2 < ... < x n x_1 < x_2 < \ldots < x_n x1<x2<...<xn on the X-axis of the plane. For each 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n, you have to pick a real value r i > 0 r_i > 0 ri>0 and draw a disk with radius r i r_i ri and center x i x_i xi such that no two disks overlap.

You want to choose values r i r_i ri in such a way that the number of tangent pairs of disks is maximized, and you have to find this maximum value.

We say that two disks overlap if the area of their intersection is positive. In particular, two outer tangent disks do not overlap.

笔者的思路是直接从头和从尾开始遍历,处理一个前缀和后缀能填多少个,然后直接枚举分界点,主要是tm题目中限制半径不能是0,于是就设置了一个很小很小的double数字充当半径,然后遍历处理上界和下界,大概这样的思路。

cpp 复制代码
#define double long double

void solve()
{
    int n;
    cin >> n;
    vector<int> a(n + 1), pre(n + 1), suf(n + 1);
    vector<vector<double>> up(2, vector<double>(n + 1)), lo(2, vector<double>(n + 1));
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    if (n == 1) {
        cout << 0 << endl;
        return;
    } else if (n == 2) {
        cout << 1 << endl;
        return;
    }
    a.push_back(INF);

    lo[0][1] = lo[1][n] = 0.000000001;
    up[0][1] = a[2] - a[1], up[1][n] = a[n] - a[n - 1];
for (int i = 2; i <= n; i++) {
        double d = a[i] - a[i - 1];
        lo[0][i] = d - up[0][i - 1];
        // if (lo[0][i] < 1e-12) lo[0][i] = 0.000000001;
        up[0][i] = min((double)a[i + 1] - a[i], (double)d - lo[0][i - 1]);

        if (lo[0][i] <= up[0][i] && up[0][i] > 0) {
            pre[i] = pre[i - 1] + 1;
        } else {
            lo[0][i] = 0.000000001; 
            pre[i] = pre[i - 1];
        }
    }

    a[0] = -INF;
    for (int i = n - 1; i >= 1; i--) {
        int d = a[i + 1] - a[i];
        lo[1][i] = d - up[1][i + 1];
        // if (lo[1][i] < 1e-12) lo[1][i] = 0.000000001; 
        up[1][i] = min((double)a[i] - a[i - 1], (double)d - lo[1][i + 1]);

        if (lo[1][i] <= up[1][i] && up[1][i] > 0) {
            suf[i] = suf[i + 1] + 1;
        } else {
            lo[1][i] = 0.000000001;
            suf[i] = suf[i + 1];
        }
    }

    int ans = max(pre[n], suf[1]);

    // debug(ans);
    for (int i = 2; i < n; i++) {
        double L = max(lo[0][i], lo[1][i]);
        double R = min(up[0][i], up[1][i]);
        if (L <= R && R > 0) {
            ans = max(ans, pre[i - 1] + suf[i + 1] + 1);
        }
    }

    for (int i = 1; i < n; i++) {
        ans = max(pre[i] + suf[i + 1], ans);
        int d = a[i + 1] - a[i];
        if (up[1][i + 1] > 0 && up[0][i] > 0 && up[1][i + 1] + up[0][i] >= d + 1e-12 && lo[1][i + 1] + lo[0][i] <= d - 1e-12) {
            ans = max(pre[i] + suf[i + 1] + 1, ans);
        }

    }

    cout << ans << endl;
}

E. No Effect XOR

time limit per test: 2 seconds

memory limit per test: 256 megabytes

In the jungle, there is a lake with infinite lily pads on it. The lily pads are numbered with non-negative integers 0 , 1 , 2 , 3 , ... 0, 1, 2, 3, \ldots 0,1,2,3,.... The lily pads with numbers between l l l and r r r inclusive are called suitable, while all other lily pads are not suitable for the frogs to sit on.

Currently, a single frog is sitting on each suitable lily pad.

Ostad is watching the lake and wants to reorder the frogs. To do so, Ostad can pick a positive integer x x x and announce it to the frogs. After hearing the number, the frog sitting on the i i i-th lily pad will jump to the ( i ⊕ x ) (i \oplus x) (i⊕x)-th one, where ⊕ \oplus ⊕ denotes the bitwise XOR operation.

Ostad likes the frogs, and therefore he wants to pick the number x x x in such a way that all frogs stay within the range of suitable lily pads.

Help Ostad by counting how many different numbers x x x Ostad can choose such that no frog jumps outside the suitable segment of the lily pads.

看了官方题解研究了好一会儿才看懂到底是怎么做的...

首先要有几个对于 X O R XOR XOR 以及位运算的理解:

  1. l o w b i t ( x ) lowbit(x) lowbit(x):返回 x x x 二进制表示下,最后一个是1的数字是多少,也可以理解为,最大的能整除x的2的整数次幂数字 。eg: l o w b i t ( 12 ) lowbit(12) lowbit(12) 就返回 4 4 4,因为 4 4 4 是最大的能整除 12 12 12 的 2 2 2 的整数次幂数字。

  2. X O R XOR XOR运算具有某个神奇的分块映射性质:例如对于长度 16 的数组,从 0 ~ 16 ; p = [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ] p = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] p=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]

    所有元素对 4 4 4 异或的结果: p = [ 4 , 5 , 6 , 7 , 0 , 1 , 2 , 3 , 12 , 13 , 14 , 15 , 8 , 9 , 10 , 11 ] p = [4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11] p=[4,5,6,7,0,1,2,3,12,13,14,15,8,9,10,11]

    所有元素对 5 5 5 异或的结果: p = [ 5 , 4 , 7 , 6 , 1 , 0 , 3 , 2 , 13 , 12 , 15 , 14 , 9 , 8 , 11 , 10 ] p = [5,4,7,6,1,0,3,2,13,12,15,14,9,8,11,10] p=[5,4,7,6,1,0,3,2,13,12,15,14,9,8,11,10]

    可以发现对于 [0, 7] 之间的八个元素依旧在前8个的位置,相当于一个长度为 8 的块(窗口)。

  3. 同样这里又可以引申出一个基的概念:即,如果我们把所有的元素对 4 异或之后再次对于 5 异或,其实元素依然不会跑出自己所在的八个元素构成的块。所以存在一组基础的数字使得对于任意一些数字异或之后依然不会跑出所在的原有块的范围,这一组最少的数字就是。(有点像线性基)

综上,我们就对需要找出如何分块,使得 l l l 或者 r r r 都不会把一个块分割成两部分。或者,l,r正好是和中间的部分对称,那么就会多一个基。

大概如上,具体可以参考官方题解

cpp 复制代码
inline int lowbit(const int &x) { return x & -x; }
inline int check(const int &x) { return x == lowbit(x); }

void solve()
{
    int l, r;
    cin >> l >> r;
    int bit = 1LL << (max(__lg(l), __lg(r)));

    while ((bit & l) == (bit & r) && bit > 0) {
        if (bit & l) l -= bit, r -= bit;
        bit >>= 1LL;
    }

    if (l == 0) {
        cout << lowbit(r + 1) - 1LL << endl;
    } else if (check(l + r + 1)) {
        cout << 2LL * min(lowbit(r + 1), lowbit(l)) - 1LL << endl;
    } else {
        cout << min(lowbit(r + 1), lowbit(l)) - 1LL << endl;
    }
}
相关推荐
燃于AC之乐3 小时前
我的算法修炼之路--4 ———我和算法的爱恨情仇
算法·前缀和·贪心算法·背包问题·洛谷
MM_MS8 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
独自破碎E9 小时前
【二分法】寻找峰值
算法
mit6.8249 小时前
位运算|拆分贪心
算法
ghie90909 小时前
基于MATLAB的TLBO算法优化实现与改进
开发语言·算法·matlab
恋爱绝缘体19 小时前
2020重学C++重构你的C++知识体系
java·开发语言·c++·算法·junit
wuk9989 小时前
VSC优化算法MATLAB实现
开发语言·算法·matlab
Z1Jxxx10 小时前
加密算法加密算法
开发语言·c++·算法
乌萨奇也要立志学C++10 小时前
【洛谷】递归初阶 三道经典递归算法题(汉诺塔 / 占卜 DIY/FBI 树)详解
数据结构·c++·算法
vyuvyucd11 小时前
C++引用:高效编程的别名利器
算法