Codeforces Round #782 A-D题解

个人思路,仅作记录,可以参考,欢迎交流。

比赛地址:传送门

(太忙所以没打,结果赛后随便做了一下前四题发现居然很简单,亏了)


A. Red Versus Blue

传送门

【题意】求一个只含r个’R’和b个’B’(r>b),且「最长同字符子串」最短的字符串。

【思路】将r个’R’尽可能平均地分为b+1份,每一份用’B’隔开。

【代码】

void solve()
{
	int n, r, b;
	cin >> n >> r >> b;
	string ans;
	for (int i = 1; i <= r % (b + 1); ++i)
	{
		if (i > 1)
		{
			ans += 'B';
		}
		for (int j = 1; j <= r / (b + 1) + 1; ++j)
		{
			ans += 'R';
		}
	}
	for (int i = 1; i <= (b + 1) - r % (b + 1); ++i)
	{
		if (ans.length())
		{
			ans += 'B';
		}
		for (int j = 1; j <= r / (b + 1); ++j)
		{
			ans += 'R';
		}
	}
	cout << ans << '\n';
	return;
}

B. Bit Flipping

传送门

【题意】给定一个二进制串和一种操作:

  • 选定二进制串中的一位,将该位以外的其他位取反

求如何对二进制串进行k次操作能得到对应数值最大的串,并求这个最大串。

【思路】若对第i位进行m次操作,则第i位会取反k-m次。所以第i位最后是否取反只与k和m的奇偶有关,只要对k的奇偶进行分类讨论,然后从高位向低位考虑,尽量在给每个0分配最少操作次数(分配1或0)的情况下使其取反为1。最后剩余的操作次数若为偶数,则分配给任一位都不影响答案;而若为奇数,则可以分配给最后一位使其从1变为0,因为这样对答案的削减最小。

【代码】

int ans[200005];

void solve()
{
	int n, k;
	int o = 0, e = 0;
	string str;
	cin >> n >> k;
	cin >> str;
	int quota = k;
	for (int i = 0; i < n; ++i)
	{
		if (k % 2)
		{
			if (str[i] == '1')
			{
				if (quota)
				{
					quota--;
					ans[i] = 1;
				}
				else
				{
					str[i] = '0';
					ans[i] = 0;
				}
			}
			else if (str[i] == '0')
			{
				ans[i] = 0;
				str[i] = '1';
			}
		}
		else//k偶
		{
			if (str[i] == '1')
			{
				ans[i] = 0;
			}
			else if (str[i] == '0')
			{
				if (quota)
				{
					quota--;
					ans[i] = 1;
					str[i] = '1';
				}
				else
				{
					ans[i] = 0;
				}
			}
		}
	}
	if (quota)
	{
		ans[n - 1] += quota;
		if (quota % 2)
		{
			str[n - 1] = (str[n - 1] == '1' ? '0' : '1');
		}
	}
	cout << str << '\n';
	for (int i = 0; i < n; ++i)
	{
		cout << (i ? " " : "") << ans[i];
	}
	cout << '\n';
	return;
}

C. Line Empire

传送门

【题意】给定n座城到城0的距离,以及两种操作:

  1. 1. 在首都为i的情况下将首都向右迁到城j,花费a*(d[j]-d[i])
  2. 2. 在首都为i的情况下占领右方第一个未被占领的城j,花费b*(d[j]-d[i])

初始时只占有0且其为首都。求最少花费多少才能占领所有的城。

【思路】对于同一起始地的迁都操作,早迁一定优于晚迁。所以迁都的目的地只考虑最新占领的城。当已经占领到城j且首都为i时,将首都迁至j的花费为a*(d[j]-d[i]),而可以为后续占领节省的花费为b*(n-j)*(d[j]-d[i])。令a*(d[j]-d[i])<b*(n-j)*(d[j]-d[i])解得j<n-a/b,即仅当j<=n-a/b时选择迁都至j,其余情况不迁,直接占领。

【代码】

long long x[200005] = { 0 };

void solve()
{
	long long n, a, b;
	cin >> n >> a >> b;
	for (int i = 1; i <= n; ++i)
	{
		cin >> x[i];
	}
	if (n == 1)//注意特殊情况
	{
		cout << b * x[1] << '\n';
	}
	else
	{
		long long ans = 0;
		long long move = n - min(n, (a / b + !!(a % b)));//注意特殊情况
		ans += a * x[move];
		ans += b * x[move];
		for (long long i = move + 1; i <= n; ++i)
		{
			ans += b * (x[i] - x[move]);
		}
		cout << ans << '\n';
	}
	return;
}

D. Reverse Sort Sum

传送门

【题意】给定一个长度为n的二进制串,由如下方法衍生出n个二进制串:第i个衍生串为将原串前i位进行从小到大排序后的结果(i=1,2,…,n)。对于每一位,已知所有衍生串上该位的值的和,求任一满足要求的原串。

【思路】观察规律。

做法为:从最后一位开始考虑,将每一位的总和减掉该位蓝色部分总和就能判断出原串该位是0还是1。下面说明如何得到每一位蓝色部分的总和。

首先,根据所有位的总和可以计算出原串中1的个数sum,则倒数前sum列的蓝色部分一定都为1。

考虑特殊情况:若原串中1都分布在右方,则蓝色部分只有倒数前i位为1,其余都为0。

在此基础上,一旦判断出原串中某一位为0,分配给蓝色部分的1就会增加。在图上体现为:这一列0会将它们左边的蓝色部分的1向左“推”(想一想,为什么?)。所以,当某一位的蓝色部分不全为0时,其内0的个数与原串中0的出现位数有关(关系如图)。

 

所以只需每次判断出0时记下0的位置信息,遇到蓝色部分有0的位时消耗最早记下的信息。如果发现已经消耗完,说明之后所有位的蓝色部分一定都为0,也即原串的之后所有位都为0。

具体见代码及注释。

【代码】

int c[200005];
int a[200005];
queue<int> q;

void solve()
{
	while (q.empty() == 0)
	{
		q.pop();
	}
	int n;
	cin >> n;
	long long sum = 0;
	for (int i = 1; i <= n; ++i)
	{
		cin >> c[i];
		sum += c[i];
	}
	sum /= n;//原串中1的个数
	for (int i = n; i >= 2; --i)
	{
		if (n - i < sum)//第i位蓝色部分全为1
		{
			a[i] = c[i] - (n - i + 1);
			if (a[i])//第i位为1
			{
				a[i] /= i - 1;
			}
			else//第i位为0
			{
				q.push(n - i + 1);
			}
		}
		else//第i位蓝色部分有0
		{
			if (q.empty() == 0)//消耗之前记录的信息
			{
				a[i] = c[i] + q.front() - (n - i + 1);
				q.pop();
				if (a[i])//第i位为1
				{
					a[i] /= i - 1;
				}
				else//第i位为0
				{
					q.push(n - i + 1);
				}
			}
			else//原串之后的位已经不可能再有1了
			{
				a[i] = 0;
			}
		}
	}
	a[1] = !!c[1];//第一位比较特殊,特判一下

	for (int i = 1; i <= n; ++i)
	{
		cout << a[i] << (i == n ? "\n" : " ");
	}
	return;
}

(好累好烦,这枯燥的学习生活里只有写代码能给我带来一丝快乐了)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值