Codeforces Round 954(Div. 3)

Codeforces Round 954(Div. 3)

\(C\). UpdateQueries

  • 对索引数组 \(ind\) 去重排序

  • 对字符串 \(c\) 排序

顺序遍历索引数组,将 \(s[ind[i]]\) 替换成 \(c[i]\)

void solve() {
	int n, m; cin >> n >> m;
	string s; cin >> s;
	vector<int> idx(m);
	string cset;
	for(int i = 0; i < m; i++) cin >> idx[i];
	cin >> cset;
	
	sort(idx.begin(), idx.end());
    idx.erase(unique(idx.begin(), idx.end()), idx.end());
    
	sort(cset.begin(), cset.end());
	
	int len = idx.size();
	for(int i = 0; i < len; i++){
		s[idx[i] - 1] = cset[i];
	}
	cout << s << endl;
}

\(D\). Mathematical Problem

本题思路来源于xrenena

贪心一手

\(n = 2\) 时,直接将这个二位数转化成数字输出即可

\(n > 2\) 且数字中存在 \(0\) 时 ,直接输出 \(0\) (除了 \(n = 3\) 的情况)

  • 这是因为可以直接乘 \(0\)

  • \(n == 3\) 时,要特判一手,如果字符串两侧出现了 \(0\) 则直接输出 \(0\)return 即可,如果两侧没有出现 \(0\) (中间出现 \(0\) 不用管),则直接交给下面的逻辑判断即可(例如 "091"、"910"

\(n>2\) 且无法直接乘 \(0\) 时:

  • 因为只能插入 \(n - 2\)个符号,所以必然会出现一个二位数,我们枚举这个二位数出现的位置
  • 将划分好的每个数字插入一个数组,遍历划分好的该数组
  • 遇到 \(v[i] = 1\) 时直接跳过,其他的全加起来
  • 最后判断,如果 \(sum = 0\),直接输出 \(1\)(因为这种情况可能是全是 \(1\) ,例如"101"被划分成 101
  • 如果 \(sum > 0\),直接输出 \(sum\) 即可
//如果数字中 n > 2 且 数字中存在0,直接输出0,对0乘法
//(但是要特别判断n = 3的情况,例如901,但是3位数中两边为0的时候也可以直接输出0)

//枚举每个2位数的位置,用一个v数组把分好的数字存起来
//遍历v数组,遇到1就跳过它,因为1用来做乘法的

void solve() {
	int n; cin >> n;
	string s; cin >> s;
	if(s.find('0') != -1 && n > 2){
		if(n == 3){//特判3位数中出现0在两侧的情况(091,910的情况)
			if(s[0] == '0' || s[2] == '0'){
				cout << 0 << endl;
				return;
			}
		}else{
			cout << 0 << endl;
			return;
		}
	}
	if(n == 2){
		cout << (s[0] - '0') * 10 + (s[1] - '0') << endl;
		return;
	}
	int res = 0x3f3f3f3f;
	//枚举二位数在哪个位置
	for(int idx = 0; idx < s.size() - 1; idx++){
		//v存放分好的数字
		vector<int> v;
		for(int i = 0; i < s.size(); i++){
			if(i == idx){//到了二位数的位置,直接将二位数放进v
				v.push_back((s[i] - '0') * 10 + (s[i + 1] - '0'));
				i++;
				continue;
			}
			v.push_back(s[i] - '0');
		}
		int sum = 0;
		//遍历加起来
		for(auto i : v){
			if(i == 1) continue;
			sum += i;
		}
		if(sum == 0) {//如果sum是0可以直接输出1,因为所有1都被跳过了
			res = min(1, res);
		}
		else res = min(sum, res);
	}
	cout << res << endl;
	
}

\(E\). Beautiful Array

方法一:贪心+滑动窗口

先判断是否能使数组美观:

  • 通过一个\(map\) 记录取模 \(k\) 的结果相同的数的个数(例如 \(3 \% 2\)\(5 \% 2\) 的结果相同)
  • 遍历 \(map\),记录余数相同的组中,元素个数为奇数的组有多少个
  • 如果个数为奇数的组 > 1,则无论如何都无法转化为美丽数组,则输出 \(-1\),直接return

否则可以转化成美丽数组:

  • 按照余数排序一下

  • 滑动窗口维护每个余数相同的组,用一个 \(v\) 数组将组内元素全装进去排个序(这样就能保证最优选择一定是相邻两个数字进行匹配)

  • 利用 \(sum\) 求两两配对的的前缀和,

    • 如下 \(s[1]\) 表示 \((v[1] - v[0]) / k\)
    • \(s[3]\) 表示 \((v[3] - v[2])\ /\ k + s[3 - 2]\)
    • \(s[2]\) 表示 \((v[2] - v[1]) / k\)

\[v:\ 1\ 4\ 10\ 22\ 25\\ s:\ 0\ 1\ 2\ 5\ 3 \]

  • 这样我们枚举不用的点,用前缀和求代价,取最小值即可
#define int long long

//自定义排序
bool compare(int x, int y, int k) {
    return (x % k) < (y % k);
}

void solve() {
	long n, k; cin >> n >> k;
	map<int, int> hash;
	vector<int> a(n);
	for(int i = 0; i < n; i++) {
		cin >> a[i];
		hash[a[i] % k]++;
	}
	int num = 0, cnt = 0;
	//算一算余数相同的组中,元素个数为奇数的组有多少个
	for(auto [k, v] : hash){
		if(v % 2) cnt++, num = k;
	}
	//如果元素个数为奇数的组大于1个,则无论如何都不能变成完美数组
	if(cnt > 1){
		cout << -1 << endl;
		return;
	}
	//按照余数排序
	sort(a.begin(), a.end(), [&](int x, int y){
		return compare(x, y, k);
	});
	int res = 0;
	//滑动窗口维护每个余数相同的组
	int i = 0, j = 1;
	while(j < n){
		//将余数相同的数全放进v数组
		vector<int> v;
		v.push_back(a[i]);
		while(j < n && a[i] % k == a[j] % k){
			v.push_back(a[j]);
			j++;
		}
		//对v中排序
		sort(v.begin(), v.end());
		//算出每隔位的前缀和
		int len = v.size();
		vector<int> sum(len);
		for(int u = 1; u < len; u++){
			if(u - 2 >= 0) sum[u] = sum[u - 2];
			sum[u] += (v[u] - v[u - 1]) / k;
		}
		//如果v中的个数为偶数,直接前后两两配对即可
		if(len % 2 == 0) res += sum[len - 1];
		else {//如果是奇数则枚举哪个点用不上
			int ans = 0x3f3f3f3f;//记录最小代价
			for(int u = 0; u < len; u += 2){
				int all = 0;
				if(u > 0) all += sum[u - 1];
				if(u < len - 1) all += sum[len - 1] - sum[u];
				ans = min(ans, all);
			}
			res += ans;
		}
		i = j;
		j++;
	}
	cout << res << endl;
}

方法二:DP

该方法来源于yuzhongrun
详细方法题解可跳转至div3E. Beautiful Array

// https://codeforces.com/contest/1986/problem/E
#include <bits/stdc++.h>
#define caseT \
    int T;    \
    cin >> T; \
    while (T--)
using namespace std;
using ll = long long;
void solve() {
    ll n, k, ans = 0, num2 = 0;
    cin >> n >> k;
    vector<ll> nums(n);
    for (int i = 0; i < n; i++) cin >> nums[i];
    sort(nums.begin(), nums.end());
    map<ll, vector<ll>> H;
    for (ll num : nums) H[num % k].push_back(num);
    for (auto &[key, p] : H) {
        if (p.size() & 1 && ++num2 >= 2) {
            cout << "-1" << endl;
            return;
        }
        int len = p.size();
        vector<vector<ll>> dp(len, vector<ll>(2));
        dp[0][0] = INT_MAX;
        if (len > 1) dp[1] = {(p[1] - p[0]) / k, INT_MAX};
        for (int i = 2; i < len; i++) {
            ll t = (p[i] - p[i - 1]) / k;
            if (i & 1) {
                dp[i][0] = min(dp[i - 2][0], dp[i - 1][1]) + t;
            } else {
                dp[i][1] = dp[i - 1][0];
                dp[i][0] = min(dp[i - 2][0], dp[i - 2][1]) + t;
            }
        }
        ans += (len & 1) ? min(dp[len - 1][1], dp[len - 1][0]) : dp[len - 1][0];
    }
    cout << ans << endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    caseT solve();
    return 0;
}
posted @ 2024-07-10 11:48  夕闻  阅读(92)  评论(0编辑  收藏  举报