Educational Codeforces Round 109 div2 A~F题解

Posted 欣君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Educational Codeforces Round 109 div2 A~F题解相关的知识,希望对你有一定的参考价值。

视频讲解:BV1MU4y1L7FB

A. Potion-making

题目大意

需要调配出精华浓度为 k % k\\% k% 的药剂,每次可以添加 1 1 1 升水或 1 1 1 升精华,求最少需要添加几次。 k k k [ 1 , 100 ] [1,100] [1,100] 范围内的整数。

题解

调配出精华浓度为 k % k\\% k% 的药剂,即水与精华的比例 a b = 100 − k k \\frac{a}{b}=\\frac{100-k}{k} ba=k100k 。易得当 a b \\frac{a}{b} ba 为最简分数, a + b a+b a+b 最小。
a = 100 − k g c d ( k , 100 − k ) a=\\frac{100-k}{gcd(k,100-k)} a=gcd(k,100k)100k b = k g c d ( k , 100 − k ) b=\\frac{k}{gcd(k,100-k)} b=gcd(k,100k)k
特别的,当 k = 100 k=100 k=100 时, 应取 a = 1 , b = 0 a=1,b=0 a=1,b=0 。在部分求解最大公约数的代码写法里,可能会出现错误结果。

参考代码

#include<bits/stdc++.h>
using namespace std;
 
int gcd(int a,int b)
{
	return a%b?gcd(b,a%b):b;
}
 
int main()
{
	int T,k,g,ans;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&k);
		g=gcd(100-k,k);
		ans=k/g+(100-k)/g;
		printf("%d\\n",ans);
	}
}

B. Permutation Sort

题目大意

给定一个 1 1 1 n n n 的排列 a [ ] a[] a[] ,每次操作可以选择一个子区间(但不能选择整个排列),随意调整该区间内数的顺序。
求最少需要操作几次,使得排列 a [ ] a[] a[] 变为递增序列。

题解

因为子区间可以随意选取,不妨假设每次都选取一个长度为 n − 1 n-1 n1 的区间,即选择区间 [ 1 , n − 1 ] [1,n-1] [1,n1] 或区间 [ 2 , n ] [2,n] [2,n]

接下来分几种情况讨论:

  1. 若原排列就是一个递增序列,则无需修改,共操作 0 0 0 次;
  2. a 1 = 1 a_1=1 a1=1 a n = n a_n=n an=n ,则只需要将区间 [ 2 , n ] [2,n] [2,n] 或区间 [ 1 , n − 1 ] [1,n-1] [1,n1] 排序即可,共操作 1 1 1 次;
  3. a 1 = n a_1=n a1=n a n = 1 a_n=1 an=1 ,则需要先操作 2 2 2 次,将 1 1 1 放到 a 1 a_1 a1 上(或将 n n n 放到 a n a_n an 上),再操作 1 1 1 次将剩余数排序,共操作 3 3 3 次;
  4. 对于其他情况,则操作一次将 1 1 1 放到 a 1 a_1 a1 上(或将 n n n 放到 a n a_n an 上),再操作 1 1 1 次将剩余数排序,共操作 2 2 2 次;

参考代码

#include<bits/stdc++.h>
using namespace std;
 
const int MAXN=60;
int a[MAXN];
 
int main()
{
	int T,n,flag,i;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		flag=1;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]!=i)
				flag=0; 
		}
		if(flag)
			printf("0\\n");
		else if(a[1]==1||a[n]==n)
			printf("1\\n");
		else if(a[1]==n&&a[n]==1)
			printf("3\\n");
		else
			printf("2\\n");
	}
}

C. Robot Collisions

题目大意

n n n 个机器人在数轴上。数轴上坐标为 0 0 0 m m m 的位置为墙壁。
i i i 个机器人初始在整数坐标 x i ( 0 < i < m ) x_i(0< i < m) xi(0<i<m) 上,并朝左(朝向原点)或朝右(朝向无穷大)以每秒 1 1 1 个单位的速度前进。机器人初始所在位置不重复。
每当机器人抵达墙壁时,会立刻调转方向,以同样的速度沿相反方向前进。
每当两个机器人同时抵达同一整数坐标时,他们会发生碰撞并爆炸。如果在非整数坐标相遇,则不会爆炸。
求每个机器人是否会爆炸,若会则输出爆炸时刻,否则输出 − 1 -1 1

题解

首先注意到一点,就是只有在整数坐标相遇时才会爆炸。因此可以把所有机器人按照初始坐标的奇偶性分为两类,只有相同奇偶性坐标的机器人之间才有可能碰撞。

接下来考虑相同奇偶性的初始坐标的机器人之间的碰撞的问题。
如果不考虑两侧的墙,会发现这就是一个左右括号匹配的问题。

  • 括号匹配问题:给定一个由左右括号构成的字符串,求合法的最大左右括号匹配数。通常采用栈来解决。

朝右走的机器人代表左括号,朝左走的机器人代表右括号,求这个括号字符串直接的相互匹配关系。这个问题可以用栈解决。碰到左括号则将其放入栈中,碰到右括号则将其与栈顶的左括号匹配(没有则无配对)。

然后加入对墙壁的考虑。如果当前栈空,即当前机器人左侧没有其他机器人,或者左侧的机器人都两两碰撞消灭掉了(即括号均匹配成功),那么即使当前机器人是朝左走,也是可以加入栈中与其他朝左走的机器人进行匹配的。因为当前机器人朝左走到头后撞墙,会调转方向变为朝右走。即对于最左侧的机器人来说,不管其初始是朝左走还是朝右走,都可以变化为朝右走。这样就可以部分解决撞墙问题了。
若最左侧尚未匹配的机器人位于 x i x_i xi 坐标朝左走,我们可以将坐标 0 0 0 处的墙壁当作镜子,将其等价于位于 − x i -x_i xi 坐标朝右走。这样就能作为左括号加入栈中参与匹配。

如上从左到右遍历后,可能会出现栈中元素个数 ≥ 2 \\geq2 2 的情况,即有至少 2 2 2 个机器人是朝右走的。那么我们可以不断取出栈顶的两个元素,即最右侧的两个机器人,他们必会在最右侧的机器人撞墙反向后,相互发生碰撞。
同样将坐标 m m m 处的墙当作镜子,位于 x i x_i xi 坐标的机器人向右走,等价于位于 2 m − x i 2m-x_i 2mxi 坐标的机器人向左走。
这样不断取出栈顶两个元素,计算他们的碰撞时刻,直到栈中的元素个数 ≤ 1 \\leq 1 1,即只剩最多 1 1 1 个机器人时,结束计算。

参考代码

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

const int MAXN=300300;
struct Robot
{
	int id,x;
	char dir;
	bool operator <(Robot r)
	{
		return x<r.x;
	}
}a[MAXN],stk[MAXN];

int m;
int ans[MAXN];
vector<Robot> v0,v1;

void cal(vector<Robot>& vec)
{
	int cnt=0,i;
	for(i=0;i<vec.size();i++)
	{
		if(cnt==0||vec[i].dir=='R')
		{
			if(vec[i].dir=='L')
				vec[i].x=-vec[i].x;
			stk[cnt++]=vec[i];
		}
		else
		{
			Robot pre=stk[--cnt];
			ans[vec[i].id]=ans[pre.id]=(vec[i].x-pre.x)/2;
		}
	}
	while(cnt>=2)
	{
		Robot r=stk[--cnt];
		Robot l=stk[--cnt]Educational Codeforces Round 7 A

Educational Codeforces Round 7

Educational Codeforces Round 90

Educational Codeforces Round 33

Codeforces Educational Codeforces Round 54 题解

Educational Codeforces Round 27