ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #794 (Div2) A~E题解

2022-06-03 04:00:08  阅读:237  来源: 互联网

标签:794 BA int 题解 sum Codeforces AB now cout


https://codeforces.com/contest/1686

好久没写题了,写着玩玩,发现思维有所下滑

A题

题意:
给你\(n\)个数,每次操作可以选择\(n - 1\)个数,将这\(n - 1\)个数变成它们的平均值,问你能否在有限次操作内使得所有数相等。

思路:
看所有数的总和的平均数在序列里有没有就行了。

int a[N];
 
void solve() {
	int n;
	cin >> n;
	int s = 0;
	for(int i = 1 ; i <= n ; i ++) cin >> a[i], s += a[i];
	bool flag = false;
	if(s % n == 0) {
		s /= n;
		for(int i = 1 ; i <= n ; i ++)	
			if(a[i] == s) {
				flag = true;
				break;
			}	
	}
	
	if(flag) cout << "YES\n";
	else cout << "NO\n";
}

B题

题意:
给你一个数组,问你最多能把这个数组划分成多少个连续的奇数子数组
奇数数组是指这个数据的逆序对数目为奇数

思路:
为了使段数更多,每段区间当然是越短越好。因此直接贪心,每次出现一个递减的数就截断。

int a[N];
 
void solve() {
	int n;
	cin >> n;
	for(int i = 1 ; i <= n ; i ++) cin >> a[i];
	
	int cnt = 0;
	int now = 0;
	for(int i = 1 ; i <= n ; i ++) {
		if(a[i] >= now) now = a[i];
		else {
			cnt ++;
			now = 0;
		}
	}
	cout << cnt << "\n";
}

C题

题意:
给你一个数组,把它当做环,问你能否通过随意排序使得这个环满足每个数要么比相邻的数都大,要么比相邻数都小。

思路:
首先,\(n\)为奇数肯定不行,因为首尾会矛盾。
要构造这种锯齿状,我们只需要排序后,奇偶位置交替放大的和小的数就行,然后检验即可。

int a[N];
int b[N];
 
void solve() {
	int n;
	cin >> n;
	for(int i = 1 ; i <= n ; i ++) cin >> a[i];
	
	if(n & 1) cout << "NO\n";
	else {
		sort(a + 1, a + n + 1);
		int l = 1, r = n / 2 + 1;
		int now = 1;
		for(int i = 0 ; i < n / 2 ; i ++){
			b[now ++] = a[l + i];
			b[now ++] = a[r + i];
		}
		bool flag = true;
		for(int i = 1 ; i <= n ; i ++) {
			int l, r;
			l = i - 1, r = i + 1;
			if(l == 0) l = n;
			if(r == n + 1) r = 1;
			if((b[i] > b[l] && b[i] > b[r]) ||
				(b[i] < b[l] && b[i] < b[r])) continue;
			else {
				flag = false;
				break;
			}
		}
		if(flag) {
			cout << "YES\n";
			for(int i = 1 ; i <= n ; i ++) cout << b[i] << " ";
			cout << "\n";
		}	
		else cout << "NO\n";
	}
}
 

D题

题意:
给你四种基串 A, B, AB, BA的数目,和一个目标串S,问你能否通过基串任意顺序组合得到目标串S。

思路:
首先看看目标串和所给基串的A,B字符数目是否相等,不相等直接\(NO\)
假如我们能把AB和BA串在目标串S的填充好,那么剩下的位置是确定的,因此我们只需要考虑AB和BA串怎么放就行。
按照相邻不同的规则对目标串进行拆分,我们可以得到如下四种串
1.ABAB
2.BABA
3.ABABA
4.BABAB
显然,3号串在去除1个A后,可以由两个AB或者两个BA组成,4号串类似。
而1号和2号串则分别由两个AB、BA组成。
很容易发现ABAB串也可以转化成由BA构成,但是会损失两个字符,即 A BA B。
我们先将1号和2号串按照各自基串填充,然后用3、4号串对应去补充不足。
在此操作后,如果还有AB或BA串数目有缺失,那么考虑另一种串可能有多。
这个时候我们采取转化,将多的那一种串尽可能转化成另一种,注意一次转化整个连续串。
因为转化是有损失的,因此我们优先转化长度大的,这样才是最优的。

void solve() {
	int a, b, ab, ba;
	cin >> a >> b >> ab >> ba;
	string s;
	cin >> s;
	// a + c + d     b + c + d
	int A = 0, B = 0;
	for(int i = 0 ; i < s.size() ; i ++)
		if(s[i] == 'A') A ++;
		else B ++;
	if(A != a + ab + ba || B != b + ab + ba) {
		cout << "NO\n";
		return;
	}
	
	int cab, cba, both;
	cab = cba = both = 0;
	int st = 0;
	vector<int> AB, BA;
	for(int i = 0 ; i <= s.size() ; i ++) {
		if(i == s.size() || (i > 0 && s[i] == s[i - 1])) {	// 遇到相同的字母 
			int len = i - st;
			if(len & 1) both += len / 2;
			else {
				if(s[st] == 'A') cab += len / 2, AB.push_back(len / 2);
				else cba += len / 2, BA.push_back(len / 2);
			} 
			st = i;
		}
	}
	
	int sum = cab + cba + both;
	if(sum < ab + ba) {
		cout << "NO\n";
		return;
	}
	
	if(cab < ab){
		int d = min(both, ab - cab);
		cab += d;
		both -= d;
	}
	if(cba < ba) {
		int d = min(both, ba - cba);
		cba += d;
		both -= d;	
	}
	
	sort(AB.begin(), AB.end(), greater<int>());
	sort(BA.begin(), BA.end(), greater<int>());
	
	for(int i = 0 ; i < AB.size() ; i ++) {
		if(cab - 2 < ab) break;
		int now = AB[i];
		int d = min(cab - ab, now);
		cba += d - 1;
		cab -= d;
	}
	
	for(int i = 0 ; i < BA.size() ; i ++) {
		if(cba - 2 < ba) break;
		int now = BA[i];
		int d = min(cba - ba, now);
		cab += d - 1;
		cba -= d;
	}
	
	if(cab >= ab && cba >= ba) cout << "YES\n";
	else cout << "NO\n";
}

E题

题意:
给你一个括号串,每次操作可以选择任意一段串翻转,问你最少操作多少次使得整个括号串合法。

思路:
我们用\(1\)表示\((\),用\(-1\)表示\()\),用sum表示它们的前缀和,如果括号串合法,当且仅当对任意的i都有 \(sum[i] >= 0\)
我们找到数组中的最大\(sum[pos]\),那么我们翻转\([1, pos]\)和\([pos + 1, n]\)一定会满足条件。
因为如果翻转之后,\([1, pos]\)出现了\(sum[i] < 0\),那么\(sum[pos]\)将不会是最大前缀和,因为我们抛弃原来的\([i, pos]\)的数可以使得sum更大。
同理,区间\([pos + 1, n]\)也可以类似证明。
所以我们最多操作\(2\)次,那么考虑什么时候操作\(1\)次和\(0\)次。
显然当原串就合法就是0次。
假设区间\([l, r]\)是覆盖所有\(sum < 0\)的最小区间,那么:
如果r后面有更多的\((\)那么r应该贪心的往右边延申。也就是找到最大的\(sum[i]\)位置。
对于l,如果l左边有\()\)那么应该贪心的尽量往左边延申。也就是找到最小的\(rsum[i]\)的位置(从后往前的前缀和)。
这样我们就翻转贪心过后的\([l, r]\),如果检验合法,那么就一定可以一次翻转,否则就需要两次。

int a[N];
int sum[N], rsum[N];
char s[N];
int n;
 
bool check() {
	for(int i = 1 ; i <= n ; i ++){
		sum[i] = sum[i - 1] + a[i];
		if(sum[i] < 0) return false;
	}
	return true;
}
 
void solve() {
	
	cin >> n;
	cin >> (s + 1);
	n *= 2;
	for(int i = 1 ; i <= n ; i ++){
		if(s[i] == '(') a[i] = 1;
		else a[i] = -1;
		sum[i] = sum[i - 1] + a[i];
	}
	for(int i = n ; i >= 1 ; i --)
		rsum[i] = rsum[i + 1] + a[i];
	
	
	if(check()) {
		cout << "0\n";
		return;
	}
	
	int minv = n + 1, maxv = 0, pos = 1;
	for(int i = 1 ; i <= n ; i ++) {
		if(sum[i] < 0) {
			minv = min(minv, i);
			maxv = max(maxv, i);
		}
		if(sum[pos] < sum[i]) pos = i;
	}
	
	int l = minv, r = maxv;
	for(int i = maxv + 1 ; i <= n ; i ++)
		if(sum[r] < sum[i]) r = i;
	for(int i = minv - 1 ; i >= 1 ; i --)
		if(rsum[l] > rsum[i]) l = i;
	
	reverse(a + l, a + r + 1);
	if(check()) {
		cout << 1 << "\n";
		cout << l << " " << r << "\n";
		return;
	}
	cout << 2 << "\n";
	cout << 1 << " " << pos << "\n";
	cout << pos + 1 << " " << n << "\n";
}

标签:794,BA,int,题解,sum,Codeforces,AB,now,cout
来源: https://www.cnblogs.com/luoyicong/p/16339480.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有