24-2-27 个人赛
A - Divide and Multiply
难度: ⭐⭐(⭐)
题目大意
给定一个长度为n的数组, 现在可以对其进行无限次操作, 选择两个数a, b; 其中a是2的倍数, 然后让a /= 2, b *= 2; 问经过操作后该数组的和最大是多少;
解题思路
题意就是指因数2的转移, 而整个数组中因数2的数量是有限的; 问题在于该怎么分配这些因数2; 我们可以把所有数的因数2提出来; 然后找到化简后最大的那个数, 那个把所有因数2都分配给它; 这样的收益是最大的;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e2 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, l, r, k;
string s;
int p[N], f[N];
signed main(){
int t;
cin >> t;
while(t--){
cin >> n;
for(int i = 1; i <= n; i++) f[i] = 0;
int sum = 0, cnt = 0;
for(int i = 1; i <= n; i++){
cin >> p[i];
while(p[i] % 2 == 0){
p[i] = p[i] / 2;
f[i]++;
}
sum += p[i];
cnt += f[i];
};
sort(p + 1, p + 1 + n);
int x = p[n];
sum -= p[n];
sum += p[n] * pow(2, cnt);
cout << sum << endl;
}
return 0;
}
B - William the Vigilant
难度: ⭐⭐(⭐)
题目大意
现有一个长度为n的由字母'a' 'b' 'c'组成的字符串s; 现在进行m次操作(x, c), 把下标为x的字符改为c; 并且计算当前至少还需要修改几个字符才能让s不含有子串"abc";
注意是子串, 不是子序列;
解题思路
因为要求不能有子串是"abc", 修改时只需要修改"abc"中的一个字符就可以了; 那么我们可以统计当前字符串有几个子串"abc", 也就是需要修改几个字符; 因为子串"abc"三个字母是连续的, 所以把所有子串"abc"的下标都打上标记; 每次修改时, 如果当前下标有标记, 说明正好除掉了一个子串"abc", 然后把这个子串"abc"的标记都去掉即可, 同时子串"abc"的数量减一; 同样修改时也要看一看是不是又构成了一个子串"abc", 如果是也要打上标记;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e5 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m;
string s;
int p[N];
signed main(){
IOS;
cin >> n >> m >> s;
s = ' ' + s;
int cnt = 0;
for(int i = 1; i <= n; i++){
if(i + 2 <= n && s[i] == 'a' && s[i + 1] == 'b' && s[i + 2] == 'c'){
cnt++;
p[i] = 1;
p[i + 1] = 1;
p[i + 2] = 1;
}
}
while(m--){
int a;
char b;
cin >> a >> b;
if(s[a] == b){
cout << cnt << endl;
continue;
}
if(p[a] == 1){
p[a] = 0;
cnt--;
if(s[a] == 'a'){
p[a + 1] = 0;
p[a + 2] = 0;
}
else if(s[a] == 'b'){
p[a - 1] = 0;
p[a + 1] = 0;
}
else if(s[a] == 'c'){
p[a - 1] = 0;
p[a - 2] = 0;
}
}
s[a] = b;
if(b == 'a'){
if(a + 2 <= n && s[a + 1] == 'b' && s[a + 2] == 'c'){
p[a] = 1;
p[a + 1] = 1;
p[a + 2] = 1;
cnt++;
}
}
else if(b == 'b'){
if(a - 1 >= 1 && a + 1 <= n && s[a - 1] == 'a' && s[a + 1] == 'c'){
p[a] = 1;
p[a - 1] = 1;
p[a + 1] = 1;
cnt++;
}
}
else if(b == 'c'){
if(a - 2 >= 1 && s[a - 1] == 'b' && s[a - 2] == 'a'){
p[a] = 1;
p[a - 1] = 1;
p[a - 2] = 1;
cnt++;
}
}
cout << cnt << endl;
}
return 0;
}
C - Complex Market Analysis
难度: ⭐⭐⭐
题目大意
给定一个长度为n的数组A和一个自然数e; 问有多少个满足条件的二元组(i, k), 条件如下:
1- i和k都大于等于1;
2- i + e * k要小于等于n;
3- Ai * Ai+k * Ai+2k ... * Ai+e*k的值是质数;
解题思路
第一个条件意味着至少要两个数相乘, 第二个条件意味着一定合法, 第三个条件多个数相乘后是一个质数, 而根据质数定义, 其因子只有1和其本身, 这意味着这些相乘是数一定是一个质数和若干个1组成的; 于是就找到了解题关键; 我们首先筛出所有质数; 然后遍历数组, 如果当前是质数, 那么我就从这个位置分别往左右以e为间隔寻找包含质数在内的最长连续的1的个数: l和r; 然后当前质数的贡献就是l * r - 1; 其实就是分别在左边找一个起点, 在右边找一个终点; 减一是去掉只有质数自己的情况;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 1e6 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, cnt;
string s;
bool st[N];
int q[N], p[N];
int ini(int x){
st[0] = 1;
st[1] = 1;
int num = 0;
for(int i = 2; i <= x; i++){
if(!st[i]) q[num++] = i;
for(int j = 0; q[j] * i <= x; j++){
st[q[j] * i] = true;
if(i % q[j] == 0) break;
}
}
return num;
}
signed main(){
IOS;
ini(N - 10);
int t;;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> p[i];
int res = 0;
for(int i = 1; i <= n; i++) {
if (st[p[i]]) continue;
int l = 1, r = 1;
for (int j = i - m; j >= 1; j -= m) {
if (p[j] == 1) l++;
else break;
}
for (int j = i + m; j <= n; j += m) {
if (p[j] == 1) r++;
else break;
}
res += l * r - 1;
}
cout << res << endl;
}
return 0;
}
D - Social Network
难度: ⭐⭐⭐
题目大意
在某个会议上有n个互补相识的人, 现在有m轮操作(a, b), 每轮小莫都会介绍互不相识的两个人认识, 并且要求介绍后, a和b两人必须是相识的; 并且如果a和b相识, b和c相识, 那么a和c也会相识;
对于每轮操作后, 输出当前认识人最多的那个人所认识的人的数量;
解题思路
本题的难点是在读题, 实在是太太太难读懂了; 每轮操作其实是要在两个点之间连一条线, 这两点不能在同一个连通块里; 并且要求连完后保证a和b是相连的; 如果在此之前a和b未相连, 那么本轮操作肯定是要让a和b相互认识; 如果已经相连, 那么就可以随便找两个不在同一连通块里的点, 同时这也是本题的变量, 我们称这条边是自由边; 而题目要求的输出就是此时最大连通块的点数- 1; 所以对于自由边的使用就是要尽量把大的连通块连起来; 维护连通块点的数量很简单就不多说了; 每轮操作完后我们把所有连通块按照点的数量从大到小排序, 把最大的连通块看作主体, 设此时有x条自由边, 那么我们就可以再往后找x个连通块让其与主体相连; 注意这里说的相连只需要把他们的点的数量相加即可, 不要真的连起来, 因为自由边是看当前情况再决定连谁, 是变化的;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e3 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, m, cnt;
string s;
int p[N], num[N];
int find(int x){
if(p[x] != x){
p[x] = find(p[x]);
}
return p[x];
}
signed main(){
IOS;
cin >> n >> m;
for(int i = 1; i <= n; i++) {
p[i] = i;
num[i] = 1;
}
int maxn = 1;
for(int i = 1; i <= m; i++){
int a, b;
cin >> a >> b;
a = find(a), b = find(b);
if(a != b){
num[a] += num[b];
num[b] = 0;//注意这里一定要清零, 否则会干扰之后的排序;
p[b] = a;
if(num[a] > maxn) maxn = num[a];
}
else cnt++;
int res = maxn;
int cal[N];
memcpy(cal, num, sizeof num);
sort(cal + 1, cal + 1 + n, greater<>());
for(int j = 2; j <= cnt + 1; j++){
res += cal[j];
}
cout << res - 1 << endl;
}
return 0;
}
E - Divan and a Store
难度: ⭐(⭐)
题目大意
现有n个商品, 给出他们的价格, 小莫前来购物, 小莫只会买价格位于[l, r]之间的商品; 小莫有k元, 请问最多可以买多少件商品;
解题思路
把价格从小到大排序, 在价格区间内从便宜的开始买就行;
神秘代码
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define endl '\n'
using namespace std;
const int N = 2e2 + 10, mod = 998244353, inf = 1e18;
typedef pair<int, int> PII;
int n, l, r, k;
string s;
int p[N];
signed main(){
int t;
cin >> t;
while(t--){
cin >> n >> l >> r >> k;
int dp[N];
for(int i = 1; i <= n; i++){
cin >> p[i];
}
sort(p + 1, p + 1 + n);
int cnt = 0, sum = 0;
for(int i = 1; i <= n; i++){
if(p[i] >= l && p[i] <= r && sum + p[i] <= k){
cnt++;
sum += p[i];
}
}
cout << cnt << endl;
}
return 0;
}