Codeforces Round 905 - div.3(A B C D E F)

Codeforces Round 905 (Div. 3)

div 3 都是思维题 qwq

A. Morning

模拟光标移动即可

void solve(){
	string ss;
	cin >> ss;
	char ch = '1';
	int ans = 0;
	for(auto c : ss){
		if(c != ch){
			int x = c, y = ch;
			if(c == '0') x = '9' + 1;
			if(ch == '0') y = '9' + 1;
			ans += abs(x - y);
			ch = c;
		}
		++ ans;
	}
	cout << ans << '\n';
	return ;
}

B. Chemistry

先统计每种字符出现的次数,再统计字符出现次数为奇数的个数 \(cnt\),如果 $cnt \le k + 1 $ 则可以满足要求,反之不行

void solve(){
	int n, k;
	string ss;
	cin >> n >> k >> ss;
	vector<int> cnt(26, 0);
	for(auto c : ss){
		++ cnt[c - 'a'];
	}
	int shu[2] = {0, 0};
	for(int i = 0; i < 26; ++ i)
		if(cnt[i]) ++ shu[cnt[i] % 2];
	if(k >= shu[1] - 1) cout << "YES\n";
	else cout << "NO\n";
	return ;
}

C. Raspberries

分类讨论
k = 2,3,5 时,只有给定的数变成其的倍数才行,记录改变所需最小值即
k = 4 需特判,k = 4 = 2 * 2,我们可以利用两个奇数 2 步实现,可以利用一奇一偶 1 步实现,也可以偶数但不是 4 的倍数 2 步实现,也可以奇数 1 三步实现

void solve(){
	int n, ans = inf, k, a, shu[2] = {0, 0};
	cin >> n >> k;
	for(int i = 0; i < n; ++ i){
		cin >> a;
		ans = min(ans, (k - a % k) % k);
		++ shu[a % 2];
	}
	if(k == 4){
		if(shu[1] >= 2){
			int t = 2 - min(2, shu[0]);
			ans = min(ans, t);
		}else if(shu[1] == 1){
			if(shu[0] > 1) ans = 0;
			else if(shu[0] == 1) ans = min(ans, 1);
			else ans = min(ans, 3);
		}else{
			if(shu[0] >= 2) ans = 0;
		}
	}
	cout << ans << '\n';
	return ;
}

D. In Love

利用 multiset 存给定的线段 {l, r},用来找最大 \(l_1\) ;另外存一个 {r, l},用来找最小 \(r_1\)
当 $r_1 < l_1 $ 时满足题意,即为判断相距最远的两个线段能否相交

void solve(){
	int q, l, r;
	char ch;
	cin >> q;
	multiset<pii> x, y;
	while(q --){
		cin >> ch >> l >> r;
		if(ch == '+'){
			x.insert({l, r});
			y.insert({r, l});
		}else{
			x.erase(x.find({l, r}));
			y.erase(y.find({r, l}));
		}
		if(x.size() && x.rbegin() ->first > y.begin()->first)
			cout << "YES\n";
		else cout << "NO\n";
	}
	return ;
}

E. Look Back

由给出的数据范围可以知道,直接遍历求解显然会超数据范围,所以我们要尽量避免直接改变数值
对每一个下标的数,最终都可以表示为 $a[i] \times 2^{x_i} $

  • 如果 $a[i - 1] \le a[i] $,那么 $x_i = x_{i - 1} - cnt $,cnt 表示最小的 x 使得 $a[i - 1] \times 2^x \ge a[i] $,且当相等时 cnt 应当减一,因为题目所求的为最小操作次数
  • 如果 $a[i - 1] > a[i] $,那么 $x_i = x_{i - 1} + cnt $,cnt 表示表示最小的 x 使得 $a[i - 1] \le a[i] \times 2^x $

所以另外开一个数组存 $2^{x_i} $ 中的 \(x_i\) 即可

void solve(){
	int n;
	cin >> n;
	vector<int> a(n), b(n + 1, 0);
	for(int i = 0; i < n; ++ i) cin >> a[i];
	ll ans = 0;
	for(int i = 1; i < n; ++ i){
		if(a[i] >= a[i - 1]){
			int t = a[i - 1], cnt = 0;
			while(t < a[i]){
				++ cnt; t <<= 1;
			}
			if(t > a[i]) -- cnt;
			b[i] += max(0, b[i - 1] - cnt);
		}else{
			int t = a[i], cnt = 0;
			while(a[i - 1] > t){
				++ cnt; t <<= 1;
			}
			b[i] += b[i - 1] + cnt;
		}
		ans += b[i];
	}
	cout << ans << '\n';
	return ;
}

F. You Are So Beautiful

注意到,我们选择的子序列在原数组中出现一次,那么一定有 \(a_l\) 是数 \(a_l\) 的首次出现,\(a_r\) 是数 \(a_r\) 的末次出现,如果不是的话,两边的相同数一定可以替代 \(a_l\) 或者 \(a_r\),此时该子序列不唯一

所以问题转化为找数的首次出现和末次出现,对于一个末次出现,对答案的贡献即为其位置前(包括当前位置)首次出现的数的个数

void solve(){
	int n;
	cin >> n;
	vector<int> a(n);
	map<int, int> last;// 记录最后一个出现的位置
	for(int i = 0; i < n; ++ i){
		cin >> a[i];
		last[a[i]] = i;
	}
	map<int, int> pre;
	ll ans = 0;
	int cnt = 0;
	for(int i = 0; i < n; ++ i){
		if(pre[a[i]] == 0) ++ cnt;
		++ pre[a[i]];
		if(last[a[i]] == i) ans += cnt;
	}
	cout << ans << '\n';
	return ;
}
posted on 2023-10-23 12:03  Qiansui  阅读(284)  评论(0编辑  收藏  举报