Codeforces Round #650 (Div. 3)A~D题解

传送门

A.Short Substrings

题意:有一个字符串,从左至右列出所有长度为2的子串,然后连起来,这就是题目给的串。要我们输出原来的串。输出开头,中间隔位输出,再输出结尾。

void solve()
{
	string s;
	cin >> s;
	cout << s[0];
	for (int i = 1; i < s.size() - 1; i += 2)cout << s[i];
	cout << s[s.size() - 1] << endl;
}

B.Even Array

给你n个数,下标从0开始,可以通过交换某两个数使得每位数字和他的下标奇偶性相同,求最小交换次数,不可能的情况输出-1。我们只让不符合要求的奇数数字和偶数数字交换,所以记录这两种数字的数量,只有奇数数字个数==偶数数字个数,才能交换完,因为奇数数字和奇数数字交换是没有意义的。最后的交换次数就是奇数或者偶数的数量。

int n;
int a[55];
void solve()
{
	int odd = 0, ev = 0;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> a[i];
		if (a[i] % 2 == 1 && i % 2 == 0)odd++;
		else if (a[i] % 2 == 0 && i % 2 == 1)ev++;
	}
	if (odd == ev)cout << odd << endl;
	else cout << "-1\n";
}

C.Social Distance

题意:给一个长度为n的01串,保证每个1都相差至少 k k k个位置,即 [ i − k , i + k ] [i-k,i+k] [ik,i+k]之间都没有其他的1。问01串内还有几个0在合法的前提下可以换成1。在这里 n n n k k k都是 2 e 5 2e5 2e5级别的,所以纯暴力是不行的。我们枚举串的每一位,如果他是0,判断能不能变成1,如果能,计入答案。如果是1,直接跳到 i + x + 1 i+x+1 i+x+1

#define vint vector<int>
#define pb push_back
#define clr(a, b) memset((a), (b), sizeof(a))
int n, x;
void solve()
{
	clr(vis, 0);
	cin >> n >> x;
	string s; cin >> s;
	vint V;
	for (int i = 0; i < s.size(); i++)
	{
		if (s[i] == '1')i += x;//直接跳过k个格子
		else
		{
			bool f = 0;
			int p = -1;//p记录右边最近的1
			//我们是从左边枚举过来的,可以保证左边k位里是没有1的,否则一定会跳过现在这个点
			for (int j = i + 1; j < s.size() && j <= i + x; j++)
			{
				if (s[j] == '1')//有1就break
				{
					p = j;
					f = 1;
					break;
				}
			}
			//这里说明现在这个0可以换成1,这个点变成1后,理所当然往后跳k个格子
			if (!f)
			{
				V.pb(i);
				s[i] = '1';
				i += x;
			}
			else i = p + x;//直接跳到右边第一个1的再右边x位
		}
	}
	cout << V.size() << endl;
}

D.Task On The Board

题意:给你一个字母串 s s s,你需要构造一个长度为 m m m的字符串 t t t t t t应为 s s s在删掉任意位字母之后的任意一个排序。再给你一个含有 m m m个数字的 b b b数组, b i b_i bi是 在 t t t中,所有比 t i t_i ti大的字母和 t i t_i ti的相对位置的和。
我们以test4为例:
ecoosdcefr
10
38 13 24 14 11 5 3 24 17 0
我们首先可以看到, b [ 10 ] = 0 b[10]=0 b[10]=0,说明这一位是字典序最大的一位,且仅有一位。接着我们假装他不存在,那么他给其他b[i]的贡献也没了,所以我们把其他的 b i b_i bi都减去和 t i t_i ti t [ 10 ] t[10] t[10]的相对位置,但是要注意,这一个位置还是存在的,只是我们选择性忽视它给的贡献。那么 b i b_i bi变为下面这个数组:
29 5 17 8 6 1 0 22 16 _
我们发现,又有新的0出现,这个位置就是字典序第二大的字母,同时我们保留上一轮的0的位置,继续重复上面的操作:
23 0 13 5 4 0 _ 21 14 _
这里是有两个0,所以剩下的每位置要删除两个贡献。
为了看的更清楚,做了张图。
在这里插入图片描述
我们就能凭借0出现的先后顺序,得到他们的字典序大小排序,再去 s s s里找足够数量的字母。那么这里的顺序(下标)就是:10->7->2,6->5>4,9->3>1,8。

#define vint vector<int>
#define pb push_back
#define clr(a, b) memset((a), (b), sizeof(a))
int n;
int a[55];
bool vis[55];
int num[55];
void solve()
{
	clr(num, 0);
	clr(vis, 0);
	string s; cin >> s;
	for (int i = 0; i < s.size(); i++)num[s[i] - 'a']++;
	cin >> n;
	for (int i = 1; i <= n; i++)cin >> a[i];
	vint V, res[55];//V是这一轮新的0的下标,res是每一轮新出的0的下标,其实感觉可以合并成一个
	int pos = 0, now = 0;//pos是记录当前的轮数,now是记录已经有几个0了,当有n个0时,就是while循环退出的时候
	while (1)
	{
		V.clear();
		for (int i = 1; i <= n; i++)
			if (!vis[i] && a[i] == 0)
			{
				V.pb(i);
				vis[i] = 1;
				res[pos].pb(i);
				now++;
			}
		pos++;
		for (int i = 1; i <= n; i++)
			if (a[i] > 0)
				for (int p : V)a[i] -= abs(i - p);
		if (now >= n)break;
	}
	string ans;
	ans.resize(n);
	int j = 0;//j是当前选到的字母的位置,不会重置为0,因为我们是从最小的字母开始找,到后面要保证所选的字母的字典序要越来越大
	//下面找数量够的字母
	for (int i = pos - 1; i >= 0; i--)
	{
		int sz = res[i].size();//表示现在我们要找的字母的数量一定要大于等于sz
		for (j; j < 26; j++)
		{
			if (num[j] >= sz)
			{
				for (int p : res[i])ans[p] = (char)('a' + j);
				j++;//因为这里break了,不会执行j++,这里要我们再写一遍
				break;
			}
		}
	}
	for (int i = 1; i <= n; i++)cout << ans[i];
	cout << endl;
}

E.Necklace Assembly

明天看看

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值