2A.Shuffle Party(枚举)
题意:
给你一个数组a1,a2,…,ana_1,a_2,\ldots,a_na1,a2,…,an。最初,每个1≤i≤n1\le i\le n1≤i≤n都有ai=ia_i=iai=i。
整数k≥2k\ge2k≥2的运算swap(k)\texttt{swap}(k)swap(k)定义如下:
- 设ddd是不等于kkk的kkk的最大除数†^\dagger†。然后交换元素ada_dad和aka_kak。
假设你按照这样的顺序对每一个i=2,3,…,ni=2,3,\ldots,ni=2,3,…,n进行swap(i)\texttt{swap}(i)swap(i)。找出111在数组中的位置。换句话说,在执行这些操作后,找出jjj和aj=1a_j=1aj=1的位置。
†^\dagger† 如果存在一个整数zzz使得y=x⋅zy=x\cdot zy=x⋅z是yyy的整除数,那么整数xxx就是yyy的整除数。
分析:
关注111的位置可以发现规律。111可以换到位置222,位置222只能换到位置444,位置444换到位置888,以此类推发现换到的位置都是222的幂次,枚举一下最大的幂次即可。
代码:
#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×n2\times n2×n的网格,网格中充满了"0"和"1"。假设第iii行和第jjj列的交叉点上的数字是ai,ja_{i,j}ai,j。
在左上角的(1,1)(1,1)(1,1)网格中有一只蚱蜢,它只能向右或向下跳一格。它想到达右下方的(2,n)(2,n)(2,n)网格。考虑一个长度为n+1n+1n+1的二进制字符串,它由路径单元格中的数字按经过的顺序组成。
题目目标是
- 通过选择任意一条可用路径,找出字典序最小的†^\dagger†字符串;
- 找出能得到这个字典序最小字符串的路径数。
†^\dagger†如果两个字符串sss和ttt的长度相同,那么当且仅当在sss和ttt不同的第一个位置上,字符串sss的元素小于ttt中的相应元素时,sss在词法上小于ttt。
分析:
由于每一次只能往下和往右,所以其实我们只需要找到一个特殊点,从这个点往下走即可。如果右侧字符小于下面字符,一定向右走,这个位置记为xxx。如果右侧字符大于下面字符,此时一定得向下走,这个位置记为yyy。
我们在[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(数学)
题意:
本题是一个互动问题。
有一个秘密序列p0,p1,…,pn−1p_0,p_1,\ldots,p_{n-1}p0,p1,…,pn−1,它是{0,1,…,n−1}\{0,1,\ldots,n-1\}{0,1,…,n−1}的排列组合。
您需要找到任意两个索引iii和jjj使pi⊕pjp_i\oplus p_jpi⊕pj最大化。
为此,可以提出查询。每个查询的形式如下:可以选择任意的索引aaa、bbb、ccc和ddd(0≤a,b,c,d<n0\le a,b,c,d\lt n0≤a,b,c,d<n)。(0≤a,b,c,d<n0\le a,b,c,d \lt n0≤a,b,c,d<n)。接下来会计算x=(pa∣pb)x=(p_a\mid p_b)x=(pa∣pb)和y=(pc∣pd)y=(p_c\mid p_d)y=(pc∣pd)。最后,你会得到xxx和yyy的比较结果。换句话说,你会被告知是x<yx\lt yx<y、x>yx\gt yx>y还是x=yx=yx=y。
请找出任意两个索引iii和jjj(0≤i,j<n0\le i,j\lt n0≤i,j<n),使得pi⊕pjp_i\oplus p_jpi⊕pj在所有这样的索引对中最大,最多可以有3n3n3n个查询。如果有多对索引满足条件,输出其中任何一个。
分析:
这道题考虑从特殊的地方入手,题目中给的数组是一个从000到n−1n-1n−1的排列,那么最大的数显然是n−1n-1n−1,然后要找出n−1n-1n−1为000的哪些位置为111的数,显然两者进行∣\mid∣运算的值应该最大,但是如果仅仅是找或运算的话,那么n−1n-1n−1为111的位置,且找到的数对应位置也为111的位置的那种值需要排除,显然我们的目标值中111的个数最少,目标值为111的位置所有找到的值对应的位置应该也是111,但是应该比目标值大。所以就要找到所有值中最小的一个。比较两个数的大小可以通过询问a,a,b,ba,a,b,ba,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(思维)
题意:
有一个长度为nnn的一维网格。网格的第iii个单元格包含一个字符sis_isi,该字符可以是’<‘或者’>'。
当一个弹球被放置在其中一个单元格上时,它会按照以下规则移动:
- 如果弹球位于第iii个单元格上,且sis_isi为’<‘, 弹球下一秒向左移动一个单元格。如果sis_isi 是’>',则向右移动一个单元格。
- 弹球移动后,字符sis_isi将被反转(即如果sis_isi以前是’<‘,将变为’>',反之亦然)。
- 当弹球离开网格时,它将停止移动:无论是从左边界还是从右边界。
本题需要回答nnn个独立查询。在第iii次查询中,弹球将被放置在第iii个单元格中。请注意,我们总是在初始网格上放置一个弹球。
对于每个查询,计算弹球离开网格需要多少秒。可以证明,弹球总是会在有限步数内离开网格。
分析:
会改变最初放置在位置ppp的弹球的方向的单元格是ppp左边的’>‘和ppp右边的’<'。
为方便起见,假设sps_psp为’>'、k=min(countright(1,p),countleft(p+1,n))k=min(countright(1,p),countleft(p+1,n))k=min(countright(1,p),countleft(p+1,n)),弹球从左边界离开(其他情况也可以类似方法处理)。
可以通过前缀相加+二进制搜索得到right[1,…,k]right[1,\ldots,k]right[1,…,k]和left[1,…,k]left[1,\ldots,k]left[1,…,k],其中rightrightright表示ppp左边的’>‘的单元格序列(按递减顺序),leftleftleft表示ppp右边的’<'的单元格序列(按递增顺序)。
用rightrightright和leftleftleft来描述弹球的轨迹:
-
第一段:弹球从right1right_1right1移动到left1left_1left1;
-
第二段:弹球从left1left_1left1移动到right2right_2right2;
-
第三段:弹球从right2right_2right2移动到left3left_3left3;
-
…\ldots…
-
第2k2k2k段:弹球从leftkleft_kleftk移动到左边界。
不难发现,可以使用前缀和来存储序列之和,然后快速计算弹球移动的时间。
代码:
#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,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

编程竞赛题目解析:数组操作、路径优化与逻辑谜题,
522

被折叠的 条评论
为什么被折叠?



