CF1659 Codeforces Round #782 (Div. 2) 题解

之前说过的题解,E应该不会补了(大概)

A Red Versus Blue

题意非常简单,构造题。给定r个红色气球和b个蓝色气球,将它们排成一排,要求使得连续出现的最多的同色气球最少,题目保证b严格小于r。然而这道题我却做的令人感叹。

数据范围:1t1000;3n100;1b<rn;

首先,可以看出这个构造出的最大的最小数量是rb+1。因为这个r可以被分成b+1份。

然后我的大脑就此停止工作了,开始进行了丑陋的模拟。主要思路是,求出来mx=nb+1,直接先按照[mxr+1b]来分配,等到不满足r>b and rmx时,再按照[mxr+1b]分配,最后输出剩下的b。属实是三次wa3之后的无奈之举。

void solve(){
	scanf("%d%d%d", &n, &r, &b);
	int mx = n / (b + 1);
	while (r > b && r >= mx && b > 0){
		for (int i=0;i<mx;++i) printf("R");
		printf("B");
		r -= mx, b--;
	}
	while (b > 0 && r > 0){
		printf("RB");
		r--, b--;
	}
	for (int i=0;i<b;++i){
		printf("B");
	}
	for (int i=0;i<r;++i){
		printf("R");
	}
	puts("");
//	printf("!!  %d %d\n", r, b);
}

题解是这么解决的:令p=rb+1q=rmod(b+1)。然后自然构造两类字符串:[(p+1)r+1b][pr+1b]。代码也非常的简洁漂亮,不禁让我想起了之前一个A题的扫地机器人dalao的绝妙模拟。

这样,红色气球的数量即为(p+1)q+p(bq)+p=p(b+1)+q

int n, r, b;
 
void solve(){
	scanf("%d%d%d", &n, &r, &b);
	int p = r / (b + 1), q = r % (b + 1);
	for (int i=0;i<q;++i){
		cout << string(p + 1, 'R') << "B";
	}
	for (int i=q;i<b;++i){
		cout << string(p, 'R') << "B";
	}
	cout << string(p, 'R') << endl;
}

B Bit Flipping

有一个长度为n的01字符串s,必须进行确切的k次操作。对于每次操作,我们都要选择一个位置i,使得s[i]的值不变,字符串中其他位置的值反转。需要输出在k次操作后,字典序最大的字符串,并输出会在每个位置执行操作多少次。

数据范围:1t1000;1n2×105;0k109

我们可以推知一些对解题必要的结论。首先,对于一个给定的bit,当他不被选择的时候,就会反转。并且,如果它被选择2次,那么就相当于没有被选择。这样的话,fi只可能有1,或者偶数两种取值。

而且,第i个bit将会被反转kfi次。

将集合划分为如下情况:

  • k是偶数,s[i]=0,则fi=0
  • k是偶数,s[i]=1,则fi=1
  • k是奇数,s[i]=0,则fi=1
  • k是奇数,s[i]=1,则fi=0

这样从左向右执行,直到用光k步操作,就可以将s变为1111xxx形式的字符串,字典序最大。

若执行完毕k还有剩余,显然将步数都加到最后的s[n]时,字典序最大。

void solve(){
	cin >> n >> m;
	cin >> s;
	init();
 
	int tmp = m;
	for (int i=0;i<n;++i){
		if (i == n - 1) r[i] = tmp;
		else if (tmp){
			if ((s[i] - '0') ^ (m & 1) == 0)
				r[i] = 1, --tmp;
		}
		if ((s[i] - '0') ^ ((m - r[i]) & 1)){
			ans += "1";
		}
		else ans += "0";
	}
	
	cout << ans << endl;
	for (int i=0;i<n;++i){
		cout << r[i] << " \n"[i==n-1];
	}
	
}

C Line Empire

大水题,自己动手做一下就会发现,求出以第i个地方为都城,比较最小值,就是答案。

ll n, a, b;
const int N = 2e5 + 5;
int x[N];
ll p[N];
 
void solve(){
	scanf("%lld%lld%lld", &n, &a, &b);
	for (int i=1;i<=n;++i){
		scanf("%d", &x[i]);
	}
	for (int i=1;i<=n;++i){
		p[i] = p[i - 1] + x[i];
	}
	ll mn = 3e18, cur;
	for (int i=0;i<=n;++i){
		cur = (a + b) * x[i] + b * ((p[n] - p[i]) - (n - i) * x[i]);
		mn = min(mn, cur);
	}
	printf("%lld\n", mn);
}

D Reverse Sort Sum

对于01数组A[n],设函数f(k,A)返回结果为对A[n]的前k个元素排序后的结果Bk。定义数组C[n]=k=1nBk

现给定数组C[n],确定满足条件的A[n]

数据范围:1t1000;1n2×105;0Cin

通过观察,我们可以发现,A[n]中1的数量k=1ni=1nCi。因为每个1都会在Bi中起作用1次。

再看,如果我们从后往前处理,那么可以发现A[n]的值是可以方便求出来的。

  • C[n]=n,则A[n]=1
  • C[n]=1,则A[n]=0

现在来看例1:

4
2 4 2 4

我们可以得到:

A=[1,1,0,1];
B1=[1,1,0,1];
B2=[1,1,0,1];
B3=[0,1,1,1];
B4=[0,1,1,1];

由于k=3,我们可以清楚地得到B[n]=[0,1,1,1],那么删除B[n]的影响,就可以得到新的C[n1]=[2,3,1]。相同的,我们可以判断出C[n1]=1,A[n1]=0,C[n2]=[2,2]......

通过这样处理即可得到答案。区间减1,用树状数组就可以轻松实现,复杂度为O(NlogN),但这不是必要的。我们可以通过一个类似滑动窗口的东西去确定影响何时消失。

#include <bits/stdc++.h>
using namespace std;
#define ll long long

int n;
const int N = 2e5 + 5;
int c[N], a[N], t[N];

void solve(){
	scanf("%d", &n);
	ll k = 0, L, suf = 0;
	for (int i=1;i<=n;++i){
		scanf("%d", &c[i]);
		a[i] = 0, t[i] = n;
		k += c[i];
	}
	k /= n, L = n - k + 1;
	for (int i=n;i>=1&&L<=i;--i){
		int cur = c[i] - (t[i] - i);
		if (cur == i) a[i] = 1;
		else if (cur == 1){
			L--;
			t[L] = i - 1;
		}
	}
	
	for (int i=1;i<=n;++i){
		printf(i==n?"%d\n":"%d ", a[i]);
	}
}

int main(void){
	int T;
	scanf("%d", &T);
	while (T--){
		solve();
	} 
	
	return 0;
}
posted @   跳岩  阅读(47)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示