2021牛客OI赛前集训营-普及组(第三场)解析
2021牛客OI赛前集训营-J组-3 解析
前言
这次的题目比较偏向数学。只需要把题意分析透彻,代码写出来只不过是时间的问题。
A 反码
题干:
鸡尾酒今天学习了原码反码补码的概念,现在他想要设计一个程序,能够自动把原码转换成反码。
原码转换成反码的规则:原码的第一位为符号位,若符号位为 0,则反码与原码相同。若符号位为 1,则符号位不变,将其他位全部取反。
但是写代码太累了,于是鸡尾酒将这个任务交给了你。
解析:
基础题
答案1:字符串写法
/*
* @Link: https://ac.nowcoder.com/acm/contest/20102/A
* @Date: 2021-10-09 18:37:57
* @LastEditTime: 2021-10-09 18:44:11
* @FilePath: \牛客\2021牛客OI赛前集训营-J\第三场\A 反码\anti_code.cpp
* @Method:
*/
#include <iostream>
using namespace std;
string s;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> s;
if (s[0] == '0')
cout << s;
else
{
putchar('1');
for (int i = 1; s[i]; i++)
putchar(s[i] == '0' ? '1' : '0');
}
return 0;
}
答案1:简洁写法
/*
* @Link: https://ac.nowcoder.com/acm/contest/20102/A
* @Date: 2021-10-09 18:37:57
* @LastEditTime: 2021-10-10 21:10:34
* @FilePath: \牛客\2021牛客OI赛前集训营-J\第三场\A 反码\anti_code_easiest.cpp
* @Method:
*/
#include <iostream>
int o, x;
int main()
{
scanf("%1d", &o);
printf("%d", o);
while (~scanf("%1d", &x))
printf("%d", o ? !x : x);
return 0;
}
B 异或
题干:
鸡尾酒获得了一个数组,他定义数组的价值为每个相邻元素异或之和。例如数组
[3,5,9]
的价值为(3^5)+(5^9)=18
,其中^
为二进制中的异或运算。
现在他将数组向右进行 m 次向右循环移动,每次移动 bi 位,循环移动都建立在上一次移动的基础上进行。现在他想考考你,每次循环移动过后,这个数组的价值为多少?
向右循环移动的定义:向右循环移动一位,数组中最右边的元素变成最左边的元素,其余元素全部向右移动一位。
例如数组[1,2,3,4,5]
向右循环移动两位的结果是[4,5,1,2,3]
解析:
这道题较简单,很多人给这道题打上前缀和的标签,其实前缀和都不需要。
这道题唯一的坑:
不开long long见祖宗
不开long long见祖宗
不开long long见祖宗
答案1(前缀和):
#include <iostream>
#include <deque>
const int N = 1e5 + 9;
long long n, m, sum, a[N], b[N], ans[N];
std::deque <long long> deq; // 麻烦了点,这用不着deque
int main()
{
scanf("%lld%lld", &n, &m);
for (long long i = 1; i <= n; i++)
{
scanf("%lld", a + i);
if (i > 1)
deq.push_back(a[i-1] ^ a[i]), sum += (a[i-1] ^ a[i]);
}
deq.push_back(a[n] ^ a[1]), sum += (a[n] ^ a[1]);
for (long long i = 1; i <= n; i++)
{
long long temp = *deq.rbegin();
ans[i] = sum - temp;
deq.pop_back(); deq.push_front(temp);
}
printf("%lld ", ans[1]);
for (long long i = 1; i <= m; i++)
{
scanf("%lld", b + i);
b[i] += b[i-1]; b[i] %= n; // 求前缀和
printf("%lld ", ans[b[i]+1]);
}
return 0;
}
答案2 简洁写法(某巨佬写的):
/*
* @Link: https://ac.nowcoder.com/acm/contest/20102/B
* @Date: 2021-10-10 21:19:35
* @LastEditTime: 2021-10-10 21:25:00
* @FilePath: \牛客\2021牛客OI赛前集训营-J\第三场\B 异或\xor_easiest.cpp
* @Method:
*/
#include <iostream>
const int N = 1e5 + 9;
typedef long long LL;
LL a[N], sum[N], tot;
int main()
{
int n, m, k;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++)
scanf("%lld", a + i);
for (int i = 0; i < n-1; i++)
sum[i] = a[i] ^ a[i+1], tot += sum[i];
sum[n-1] = a[n-1] ^ a[0], tot += sum[n-1];
LL s = n - 1;
printf("%lld ", tot - sum[s]);
while (m--)
{
scanf("%d", &k);
s = (s - k) % n;
if (s < 0)
s += n;
printf("%lld ", tot - sum[s]);
}
return 0;
}
C 最大公约数
题干:
鸡尾酒的数学很差,他学了很长时间的最大公约数,终于有一天他会求最大公约数了。
于是他迫不及待地向你提问——给定数轴上的区间[l,r]
,你可以从中任选两个不相同的整数,求它们的最大公约数。请问它们的最大公约数最大为多少?
解析:
undefinded
答案:
D 划分
题干:
在 DOTA2 第八届国际邀请赛(TI8)中,选手 AME 操刀的水人在关键时刻释放了一个波浪的技能,扭转局势,帮助 OG 战队获得冠军。
为了纪念这一技能,我们引出一个“波浪数组”的概念,其定义如下:
对于除首尾位置之外的元素,每一个位置要么比两侧相邻的数字小,要么比两侧相邻的数字大。
例如 [1,3,2,5,3,4] 就是一个波浪数组,而 [2,3,4,1,2] 则不是,因为第二个位置 3 比左边的数字 2 大,比右边的数字 4 小。
注意:根据定义,长度小于等于 2 的数组一定是波浪数组。
现在有一个长度为 n 的数组,你每次操作可以将任意一个位置的数字修改成任意一个新数字。我们想要将其变成一个波浪数组,请问最小的修改次数是几次?
解析:
undefinded
答案:
明天继续整理