Loading

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;
}
posted @ 2024-02-27 22:53  mostimali  阅读(2)  评论(0编辑  收藏  举报