Codeforces Round 930(Div.1)&(Div.2) 2A~2D

2A.Shuffle Party(枚举)

题意:

给你一个数组 a 1 , a 2 , … , a n a_1,a_2,\ldots,a_n a1,a2,,an。最初,每个 1 ≤ i ≤ n 1\le i\le n 1in都有 a i = i a_i=i ai=i

整数 k ≥ 2 k\ge2 k2的运算 swap ( k ) \texttt{swap}(k) swap(k)定义如下:

  • d d d是不等于 k k k k k k的最大除数 † ^\dagger 。然后交换元素 a d a_d ad a k a_k ak

假设你按照这样的顺序对每一个 i = 2 , 3 , … , n i=2,3,\ldots,n i=2,3,,n进行 swap ( i ) \texttt{swap}(i) swap(i)。找出 1 1 1在数组中的位置。换句话说,在执行这些操作后,找出 j j j a j = 1 a_j=1 aj=1的位置。

† ^\dagger 如果存在一个整数 z z z使得 y = x ⋅ z y=x\cdot z y=xz y y y的整除数,那么整数 x x x就是 y y y的整除数。

分析:

关注 1 1 1的位置可以发现规律。 1 1 1可以换到位置 2 2 2,位置 2 2 2只能换到位置 4 4 4,位置 4 4 4换到位置 8 8 8,以此类推发现换到的位置都是 2 2 2的幂次,枚举一下最大的幂次即可。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 30; i >= 0; i--) {
            if (n >= (1 << i)) {
                cout << (1 << i) << endl;
                break;
            }
        }
    }
    return 0;
}

2B.Binary Path(模拟)

题意:

给你一个 2 × n 2\times n 2×n的网格,网格中充满了"0"和"1"。假设第 i i i行和第 j j j列的交叉点上的数字是 a i , j a_{i,j} ai,j

在左上角的 ( 1 , 1 ) (1,1) (1,1)网格中有一只蚱蜢,它只能向右或向下跳一格。它想到达右下方的 ( 2 , n ) (2,n) (2,n)网格。考虑一个长度为 n + 1 n+1 n+1的二进制字符串,它由路径单元格中的数字按经过的顺序组成。

题目目标是

  1. 通过选择任意一条可用路径,找出字典序最小的 † ^\dagger 字符串;
  2. 找出能得到这个字典序最小字符串的路径数。

† ^\dagger 如果两个字符串 s s s t t t的长度相同,那么当且仅当在 s s s t t t不同的第一个位置上,字符串 s s s的元素小于 t t t中的相应元素时, s s s在词法上小于 t t t

分析:

由于每一次只能往下和往右,所以其实我们只需要找到一个特殊点,从这个点往下走即可。如果右侧字符小于下面字符,一定向右走,这个位置记为 x x x。如果右侧字符大于下面字符,此时一定得向下走,这个位置记为 y y y

我们在 [ x + 1 , y ] [x+1,y] [x+1,y]之间随时都可以向下走,这样得到的都是最小字典序字符串。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        string a, b;
        cin >> a >> b;
        a = ' ' + a;
        b = ' ' + b;
        string ans;
        int y = n;
        int x = 0;
        for (int i = 1; i < n; i++) {
            if (b[i] < a[i + 1]) {
                y = i;
                break;
            }
            if (b[i] > a[i + 1]) {
                x = i;
            }
        }
        if (y == n && x == 0) {
            ans = a + b.back();
            cout << ans << endl;
            cout << n << endl;
        } else {
            ans = a.substr(1, y) + b.substr(y);
            cout << ans << endl;
            if (x == 0) {
                cout << y << endl;
            } else {
                cout << y - x << endl;
            }
        }
    }
    return 0;
}

2C(1A).Bitwise Operation Wizard(数学)

题意:

本题是一个互动问题。

有一个秘密序列 p 0 , p 1 , … , p n − 1 p_0,p_1,\ldots,p_{n-1} p0,p1,,pn1,它是 { 0 , 1 , … , n − 1 } \{0,1,\ldots,n-1\} {0,1,,n1}的排列组合。

您需要找到任意两个索引 i i i j j j使 p i ⊕ p j p_i\oplus p_j pipj最大化。

为此,可以提出查询。每个查询的形式如下:可以选择任意的索引 a a a b b b c c c d d d( 0 ≤ a , b , c , d < n 0\le a,b,c,d\lt n 0a,b,c,d<n)。( 0 ≤ a , b , c , d < n 0\le a,b,c,d \lt n 0a,b,c,d<n)。接下来会计算 x = ( p a ∣ p b ) x=(p_a\mid p_b) x=(papb) y = ( p c ∣ p d ) y=(p_c\mid p_d) y=(pcpd)。最后,你会得到 x x x y y y的比较结果。换句话说,你会被告知是 x < y x\lt y x<y x > y x\gt y x>y还是 x = y x=y x=y

请找出任意两个索引 i i i j j j( 0 ≤ i , j < n 0\le i,j\lt n 0i,j<n),使得 p i ⊕ p j p_i\oplus p_j pipj在所有这样的索引对中最大,最多可以有 3 n 3n 3n个查询。如果有多对索引满足条件,输出其中任何一个。

分析:

这道题考虑从特殊的地方入手,题目中给的数组是一个从 0 0 0 n − 1 n-1 n1的排列,那么最大的数显然是 n − 1 n-1 n1,然后要找出 n − 1 n-1 n1 0 0 0的哪些位置为 1 1 1的数,显然两者进行 ∣ \mid 运算的值应该最大,但是如果仅仅是找或运算的话,那么 n − 1 n-1 n1 1 1 1的位置,且找到的数对应位置也为 1 1 1的位置的那种值需要排除,显然我们的目标值中 1 1 1的个数最少,目标值为 1 1 1的位置所有找到的值对应的位置应该也是 1 1 1,但是应该比目标值大。所以就要找到所有值中最小的一个。比较两个数的大小可以通过询问 a , a , b , b a,a,b,b a,a,b,b来得到。

代码:

#include<bits/stdc++.h>

using namespace std;

int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        if (n == 2) {
            cout << "! 0 1" << endl;
        } else {
            char op[2];
            int mx = 0, mi = 0;
            for (int i = 1; i < n; i++) {
                cout << "? " << mx << " " << mx << " " << i << " " << i << endl;
                cin >> op;
                if (op[0] == '<')
                    mx = i;
            }
            for (int i = 1; i < n; i++) {
                cout << "? " << mx << " " << mi << " " << mx << " " << i << endl;
                cin >> op;
                if (op[0] == '<')
                    mi = i;
                else if (op[0] == '=') {
                    cout << "? " << mi << " " << mi << " " << i << " " << i << endl;
                    cin >> op;
                    if (op[0] == '>')
                        mi = i;
                }
            }
            cout << "! " << mx << " " << mi << endl;
        }
    }
    return 0;
}

2D(1B).Pinball(思维)

题意:

有一个长度为 n n n的一维网格。网格的第 i i i个单元格包含一个字符 s i s_i si,该字符可以是’<‘或者’>'。

当一个弹球被放置在其中一个单元格上时,它会按照以下规则移动:

  • 如果弹球位于第 i i i个单元格上,且 s i s_i si为’<‘, 弹球下一秒向左移动一个单元格。如果 s i s_i si 是’>',则向右移动一个单元格。
  • 弹球移动后,字符 s i s_i si将被反转(即如果 s i s_i si以前是’<‘,将变为’>',反之亦然)。
  • 当弹球离开网格时,它将停止移动:无论是从左边界还是从右边界。

本题需要回答 n n n独立查询。在第 i i i次查询中,弹球将被放置在第 i i i个单元格中。请注意,我们总是在初始网格上放置一个弹球。

对于每个查询,计算弹球离开网格需要多少秒。可以证明,弹球总是会在有限步数内离开网格。

分析:

会改变最初放置在位置 p p p的弹球的方向的单元格是 p p p左边的’>‘和 p p p右边的’<'。

为方便起见,假设 s p s_p sp为’>'、 k = m i n ( c o u n t r i g h t ( 1 , p ) , c o u n t l e f t ( p + 1 , n ) ) k=min(countright(1,p),countleft(p+1,n)) k=min(countright(1,p),countleft(p+1,n)),弹球从左边界离开(其他情况也可以类似方法处理)。

可以通过前缀相加+二进制搜索得到 r i g h t [ 1 , … , k ] right[1,\ldots,k] right[1,,k] l e f t [ 1 , … , k ] left[1,\ldots,k] left[1,,k],其中 r i g h t right right表示 p p p左边的’>‘的单元格序列(按递减顺序), l e f t left left表示 p p p右边的’<'的单元格序列(按递增顺序)。

r i g h t right right l e f t left left来描述弹球的轨迹:

  • 第一段:弹球从 r i g h t 1 right_1 right1移动到 l e f t 1 left_1 left1

  • 第二段:弹球从 l e f t 1 left_1 left1移动到 r i g h t 2 right_2 right2

  • 第三段:弹球从 r i g h t 2 right_2 right2移动到 l e f t 3 left_3 left3

  • … \ldots

  • 2 k 2k 2k段:弹球从 l e f t k left_k leftk移动到左边界。

不难发现,可以使用前缀和来存储序列之和,然后快速计算弹球移动的时间。

代码:

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N = 1000010;
int T, n;
ll Sl[N], Sr[N], IDl[N], IDr[N];
char s[N];

int findpre(int x) {
    int L = 0, R = n + 1, M;
    while (L + 1 != R) {
        M = (L + R) >> 1;
        if (Sr[M] < x)
            L = M;
        else
            R = M;
    }
    return R;
}

int findsuf(int x) {
    int L = 0, R = n + 1, M;
    while (L + 1 != R) {
        M = (L + R) >> 1;
        if (Sl[n] - Sl[M - 1] < x) R = M;
        else L = M;
    }
    return L;
}

int main() {
    scanf_s("%d", &T);
    while (T--) {
        scanf_s("%d %s", &n, s);
        for (int i = 1; i <= n; i++) {
            Sr[i] = Sr[i - 1] + (s[i - 1] == '>');
            Sl[i] = Sl[i - 1] + (s[i - 1] == '<');
            IDr[i] = IDr[i - 1] + i * (s[i - 1] == '>');
            IDl[i] = IDl[i - 1] + i * (s[i - 1] == '<');
        }
        for (int i = 1; i <= n; i++) {
            if (s[i - 1] == '>') {
                if (Sr[i] > Sl[n] - Sl[i]) {
                    int p = findpre(Sr[i] - (Sl[n] - Sl[i]));
                    printf_s("%lld ", 2 * ((IDl[n] - IDl[i]) - (IDr[i] - IDr[p - 1])) + i + (n + 1));
                } else {
                    int p = findsuf((Sl[n] - Sl[i]) - Sr[i] + 1);
                    printf_s("%lld ", 2 * ((IDl[p] - IDl[i]) - (IDr[i] - IDr[0])) + i);
                }
            } else {
                if (Sr[i] >= Sl[n] - Sl[i - 1]) {
                    int p = findpre(Sr[i] - (Sl[n] - Sl[i - 1]) + 1);
                    printf_s("%lld ", 2 * ((IDl[n] - IDl[i - 1]) - (IDr[i] - IDr[p - 1])) - i + (n + 1));
                } else {
                    int p = findsuf((Sl[n] - Sl[i - 1]) - Sr[i]);
                    printf_s("%lld ", 2 * ((IDl[p] - IDl[i - 1]) - (IDr[i] - IDr[0])) - i);
                }
            }
        }
        printf("\n");
    }
    return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,Codeforces Round 511 (Div. 1)是一个比赛的名称。然而,引用内容没有提供与这个比赛相关的具体信息或问题。因此,我无法回答关于Codeforces Round 511 (Div. 1)的问题。如果您有关于这个比赛的具体问题,请提供更多的信息,我将尽力回答。 #### 引用[.reference_title] - *1* [Codeforces Round 860 (Div. 2)题解](https://blog.csdn.net/qq_60653991/article/details/129802687)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces Round 867 (Div. 3)(A题到E题)](https://blog.csdn.net/wdgkd/article/details/130370975)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Codeforces Round 872 (Div. 2)(前三道](https://blog.csdn.net/qq_68286180/article/details/130570952)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值