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

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

A Red Versus Blue

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

数据范围:\(1\leq t\leq 1000;3\leq n\leq 100;1\leq b<r\leq n;\)

首先,可以看出这个构造出的最大的最小数量是\(\lceil \frac{r}{b+1} \rceil\)。因为这个\(r\)可以被分成\(b+1\)份。

然后我的大脑就此停止工作了,开始进行了丑陋的模拟。主要思路是,求出来\(mx=\lfloor \frac n {b+1} \rfloor\),直接先按照\([mx*r+1*b]\)来分配,等到不满足\(r>b~and~r\geq mx\)时,再按照\([mx*r+1*b]\)分配,最后输出剩下的\(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=\lfloor \frac r {b+1} \rfloor\)\(q=r \bmod (b+1)\)。然后自然构造两类字符串:\([(p+1)*r+1*b]\)\([p*r+1*b]\)。代码也非常的简洁漂亮,不禁让我想起了之前一个A题的扫地机器人dalao的绝妙模拟。

这样,红色气球的数量即为\((p+1)q+p(b-q)+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\)次操作后,字典序最大的字符串,并输出会在每个位置执行操作多少次。

数据范围:\(1\leq t \leq 1000;1\leq n \leq 2\times 10^5;0\leq k\leq 10^9\)

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

而且,第\(i\)个bit将会被反转\(k-f_i\)次。

将集合划分为如下情况:

  • \(k\)是偶数,\(s[i]=0\),则\(f_i=0\)
  • \(k\)是偶数,\(s[i]=1\),则\(f_i=1\)
  • \(k\)是奇数,\(s[i]=0\),则\(f_i=1\)
  • \(k\)是奇数,\(s[i]=1\),则\(f_i=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\)个元素排序后的结果\(B_k\)。定义数组\(C[n]=\sum\limits _{k=1}^n B_k\)

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

数据范围:\(1\leq t \leq1000;1\leq n \leq 2\times 10^5;0\leq C_i \leq n\)

通过观察,我们可以发现,\(A[n]\)中1的数量\(k=\frac 1 n \sum\limits _{i=1}^n C_i\)。因为每个1都会在\(B_i\)中起作用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[n-1]=[2,3,1]\)。相同的,我们可以判断出\(C[n-1]=1,A[n-1]=0,C[n-2]=[2,2]......\)

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

#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 @ 2022-06-14 17:51  跳岩  阅读(42)  评论(1编辑  收藏  举报