不会吧不会吧不会吧不会真的有人520不陪npy去熬夜陪毛子打cf还要掉分被绿吧?
顶不住了,本蒟蒻也要肝题解了,蒟蒻上路如有问题请大佬评论区轻喷指教orz
传送门: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成立。
思路:
代码:
#include
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)
题意:
给定t(1 ≤ t ≤ 103)组数据,每组数据两行,第一行给定n(1 ≤ n ≤ 103),第二行给定一个长度为n的01回文字符串,保证至少有1个字符‘0’。ALICE先手BOB后手,每个回合可以选择进行两个操作之一:
当字符串变为全1串时游戏结束,消耗少的一方获胜。
思路:
博弈论。操作一是选择任意‘0’,因此操作二的翻转无意义,相当于跳过自己的回合。给定的为回文串,根据字符串长度奇偶分类讨论。
代码:
#include
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");
}
}
}
传送门: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
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的序列,其中数值相同的两个数字能凑成一对,求这个序列的所有子串中所含对数之和。
思路:
太菜了看不出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
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);
}
}