Codeforces Round #721 (Div. 2)ABC题解
Posted 灀龗䯦縷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #721 (Div. 2)ABC题解相关的知识,希望对你有一定的参考价值。
不会吧不会吧不会吧不会真的有人520不陪npy去熬夜陪毛子打cf还要掉分被绿吧?
顶不住了,本蒟蒻也要肝题解了,蒟蒻上路如有问题请大佬评论区轻喷指教orz
A. And Then There Were K
传送门:A. And Then There Were K
题意:
给定t(1 ≤ t ≤ 3⋅104)组数据,每组数据一个正整数n (1 ≤ n ≤ 109),求最大的整数k,使得n & (n−1) & (n−2) & (n−3) & … (k) = 0成立。
思路:
- 水题。令一个二进制数的最高位为第x位(x从0开始计),打表几组数据可以发现将n转为二进制表示之后的x位为1需要与x为0的数字进行&运算才能求得0,x位为0的最大数字是2x-1,而n的第0位-第x-1位在与2x~n-1进行&运算时都会变为0,因此即求2x-1。因此先预处理二进制每一位的权值再遍历寻找符合区间。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 10000010;
typedef long long ll;
typedef pair<int, int>PII;
int n, m, t, k;
ll w[N];
int main() {
cin >> t;
for (int i = 0; i <= 31; i++) {//预处理
w[i] = (ll)1 << i;
}
while (t--) {
cin >> n;
for (int i = 0; i <= 31; i++) {
if (n >= w[i] && n < w[i + 1])//寻找满足条件的区间
{
cout << w[i] - 1 << endl;
break;
}
}
}
}
B1. Palindrome Game (easy version)
传送门:B1. Palindrome Game (easy version)
题意:
给定t(1 ≤ t ≤ 103)组数据,每组数据两行,第一行给定n(1 ≤ n ≤ 103),第二行给定一个长度为n的01回文字符串,保证至少有1个字符‘0’。ALICE先手BOB后手,每个回合可以选择进行两个操作之一:
- 选择任意‘0’变为‘1’,消耗1.
- 翻转字符串,消耗0,此操作只能在字符串为非回文字符串时进行,且不能连续使用。
当字符串变为全1串时游戏结束,消耗少的一方获胜。
思路:
博弈论。操作一是选择任意‘0’,因此操作二的翻转无意义,相当于跳过自己的回合。给定的为回文串,根据字符串长度奇偶分类讨论。
- 字符串长度为偶数:初始的字符串为回文串,因此A起手的操作只能将回文串变为非回文串,B操作为对称A的操作使得字符串变回回文串,循环此操作直至字符串中只剩两个对称的’0‘,此时A、B的消耗相同,字符串为回文串,轮到A操作。A只能执行操作1,B执行翻转,A将最后一个’0‘变为’1‘,A的消耗比B大2,因此字符串长度为偶数时恒为B胜。
eg:“10000001” 循环操作至“11100111”,A “11110111”,B翻转,A “11111111” 。 - 字符串长度为奇数:特判n=1时B胜。再分类字符串中部为’0‘还是’1‘。
(1)中部为’1‘时,同字符串长度为偶数的操作,恒B胜。
(2)中部为’0‘时,A起手将中部变为’1‘,此时状态变为(1),角色互换,因此A胜。特判只有一个’0‘且正好在中部的情况,如 “101”,A先手只能操作为“111”游戏结束,B胜
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 10000010;
typedef long long ll;
typedef pair<int, int>PII;
int n, m, t, k;
string str;
int main() {
cin >> t;
while (t--) {
cin >> n >> str;
if (n == 1) {
puts("BOB");
continue;
}
if (n % 2 == 1) {
int sum = 0;
for (auto t : str) {
if (t == '0')
sum++;
}
if (str[n / 2] == '1')
puts("BOB");
else {
if (sum > 1)
puts("ALICE");
else
puts("BOB");
}
} else {
puts("BOB");
}
}
}
B2. Palindrome Game (hard version)
传送门:Palindrome Game (hard version)
题意:
同B1,差别为初始给定的字符串不一定为回文串。
思路:
先判定初始给定字符串是否为回文串,是的话同B1。否则继续按照字符串长度奇偶性进行分类讨论。
-
长度为偶数。在变为回文串之前A一直执行翻转操作,B逐一将字符串向回文串更改。在某次B操作后只剩一个’0‘字符串变为回文串时,A执行1操作将该’0‘变为回文串,此后操作同B1,角色互换,因此恒A胜。
eg:初始“ 10000111”循环操作至“11000111”,此时A操作为“11100111”,此后操作同B1 。 -
长度为奇数。特判中部为’0‘并且整个字符串只有两个’0‘时,平局。
eg.初始“10011”,A不管执行操作1还是2都是平局。
此外都是A胜,具体操作雷同。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long ll;
typedef pair<int, int>PII;
int n, m, t, k;
int w[N];
string str;
int main() {
cin >> t;
while (t--) {
scanf("%d", &n);
cin >> str;
int f = 1;
int sum = 0;
//判断是否为回文串并计算字符串中'0'的个数
for (int i = 0, j = str.size() - 1; i <= n / 2; i++, j--) {
if (str[i] != str[j]) {
f = 0;
}
if (i != j) {
if (str[i] == '0')
sum++;
if (str[j] == '0')
sum++;
} else {
if (str[i] == '0')
sum++;
}
}
//不是回文串
if (!f) {
if (n & 1) {
if ( str[n / 2] == '0' && sum == 2) {
puts("DRAW");
} else
puts("ALICE");
} else {
puts("ALICE");
}
}
//是回文串
else {
if (n == 1) {
puts("BOB");
continue;
}
if (n % 2 == 1) {
int sum = 0;
for (auto t : str) {
if (t == '0')
sum++;
}
if (str[n / 2] == '1')
puts("BOB");
else {
if (sum > 1)
puts("ALICE");
else
puts("BOB");
}
} else {
puts("BOB");
}
}
}
}
C. Sequence Pair Weight
题意:
给定t(1 ≤ t ≤ 105 )组数据,每组数据两行,第一行n(1 ≤ n ≤ 105),第二行长度为n的序列,其中数值相同的两个数字能凑成一对,求这个序列的所有子串中所含对数之和。
- 子串是指原序列去除任意长度的前缀和后缀,长度可以为0 。
思路:
太菜了看不出tag,应该就是思维题吧。 统计每一对在所有子串中出现的个数并累加。设一对对子中第一个数字下标为l,第二个数字下标为r,那么显然这对对子在所有子串中出现的个数为l *(n-r+1) 。因此输入时将之前同样数字的下标 l 取出乘上(n-r+1),再将当前下标记录到该数字的数组,但是这样如果同样数字很多的话每次取出 l 复杂度会很高会t。观察到输入到当前时(n-r+1)不变,因此可以合并同类项将之前的下标全部累加至一个数字 l,此时计算当前子串对的个数的复杂度为O(1)。序列中数字范围1~1e9很大但是序列长度小于1e5,因此需要map离散化操作。每轮开始前记得初始化map和 l 数组。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;
typedef long long ll;
typedef pair<int, int>PII;
int n, m, t, k;
int w[N];//存储序列
map<int, int>mp;//离散化操作
ll ve[N];//记录离散化后为i的原数字之前出现过的下标之和
int res;
int main() {
cin >> t;
while (t--) {
scanf("%d", &n);
//初始化
ll sum = 0;
mp.clear();
for (int i = 0; i <= res; i++)
ve[i] = 0;
res = 0;//离散化映射标记
for (int i = 1; i <= n; i++) {
scanf("%d", &w[i]);
if (!mp[w[i]])//未离散化过则赋值映射标记
mp[w[i]] = ++res;
sum += ve[mp[w[i]]] * (n - i + 1);//累加子串含当前数字的对子的总个数
ve[mp[w[i]]] += i;//更新当前数字的下标和
}
printf("%lld\\n", sum);
}
}
以上是关于Codeforces Round #721 (Div. 2)ABC题解的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #721 (Div. 2) Codeforces-1527
Codeforces Round #721 (Div. 2)
C. Sequence Pair Weight——Codeforces Round #721 (Div. 2)
Codeforces Round #721 (Div. 2) C. Sequence Pair Weight(计算贡献/STL)