Educational Codeforces Round 103 (Rated for Div. 2) 1/29

比赛链接

T1

中文题意:
T组输入,给你n个盘子,你要向里面放苹果,使得盘子中苹果总数是m的倍数。并且盘子内最多的苹果最小值是多少。并且一定要保证每个盘子都有苹果。
n , m ≤ 1 0 9 n,m\leq10^9 n,m109

如果 n ≤ m n\leq m nm,那么直接平铺盘子,直到满足倍数关系,答案就是 m / n + ( m % n = = 0 ) m/n+(m\%n==0) m/n+(m%n==0),后面加法就看要不要额外补充。
如果 n > m n>m n>m,我们调整m是第一个大于n的倍数即可。 m = m ∗ c e l i ( 1.0 ∗ n / m ) m=m*celi(1.0*n/m) m=mceli(1.0n/m)

void solve() {
	n = read(), m = read();
	if (n > m)	m = m * ceil(1.0 * n / m);
	print(m / n + (m % n != 0));
}

T2

中文题意:
T组输入,每组输入给出长度为n的序列(下标从0开始)。以及一个整数m。从第一个点开始存在超标程度,并且 b i = a i ∑ j = 0 i a j b_i=\frac{a_i}{\sum\limits_{j=0}^ia_j} bi=j=0iajai。现在你要使得全部点的超标程度都小于等于 m 100 \frac{m}{100} 100m。你可以在任意一个位置把 a i a_i ai变大一点,问你改变的总和最少需要多少。
n , m ≤ 100 , a i ≤ 1 0 9 n,m\leq 100, a_i\leq10^9 n,m100,ai109

观察发现,从下标为1到下标为n-1都有可能做为分子,既然这样为什么我们不把从来不作为分子的 a 0 a_0 a0变成我们需要的大小,这样的话某个点计算的时候,我们只调整了分母,是最可能达到目标的。在遍历过去,看看每一个点最小需要调整多少,找到想要最大的那个就是我们的答案了。

const int N = 1e6 + 7;
 
ll n, m;
ll a[N];
void solve() {
	n = read(), m = read();
	ll ans = 0, sum = read();
	rep(i, 2, n) {
		a[i] = read();
		if (100 * a[i] > sum * m) {
			ll tmp = ceil(100 * a[i] * 1.0 / m - sum);
			ans = max(ans, tmp);
		}
		sum += a[i];
	}
	print(ans);
}

T3

中文题意:
T组输入,每组输入一个n代表存在n条链。下面输入三行第一行代表C数组,第二行代表A数组,第三行代表B数组。C数组的含义是第i条链有几个节点。并且每条链只能从端点向前一条链连边,他们端点连向的点在前一条边的编号分别是 a i , b i a_i,b_i ai,bi。问最大的简单循环边数是几条。
n ≤ 1 0 5 , c i ≤ 1 0 9 n\leq10^5,c_i\leq10^9 n105,ci109

对于我们从前到后枚举链条,通过贪心做取决策即可。我们知道如果 a i ! = b i a_i!=b_i ai!=bi的话,可能存在两种情况一种是绕一圈继续往跟前一条链走,第二种是当场闭合,那么我们是从前往后枚举的,只要判断一下那种选择更优保留即可。如果 a i = b i a_i=b_i ai=bi,那么只能计算当前链长度以及两条白给的边。

void solve() {
	n = read();
	rep(i, 1, n)	c[i] = read();
	rep(i, 1, n)	a[i] = read();
	rep(i, 1, n)	b[i] = read();
	ll ans = 0, res = 0; // res代表从链的端点出发的最大值。
	rep(i, 2, n) {
		if (a[i] == b[i])	res = 2;
		else {
			if (!res) res = abs(a[i] - b[i]) + 2;
			// res更新之前保留的是前一条边端点的最大值
			else res = max(res + 2 + c[i - 1] - 1 - abs(a[i] - b[i]), abs(a[i] - b[i]) + 2);
		}
		ans = max(ans, res + c[i] - 1);
	}
	print(ans);
}

T4

中文题意:
T组输入,每组输入一个长度为n的字符串,字符串只有 L R LR LR两种字符。但是我们存在n+1个点,编号从0到n。我们输入的字符串S下标从1开始,它的第i个字符如果是 L L L,说明第i个点有一个去向i-1的有向边,如果第i个字符是 R R R,那么说明第i-1个点有个去向i的有向边。现在你没走一次路径,全部的路就会反向一次,既 i → j i\to j ij,变成 j → i j\to i ji。要你输出从下标0到n全部的点做为起点,每个起点对应可以最大走多少个点。你可以重复踩同一个点,但是贡献只算一次。
n ≤ 3 ∗ 1 0 5 n\leq3*10^5 n3105

我们首先看,如果我们通过L去到了左边的一个点,那么左边点如果还要继续前进一定只能是R。因为存在路径反转。那么同理如果通过反转之后之前的R去到了左边的点,那么下一次能走的也一定要原本是L,因为你要继续往左边走,上次走过来的方向要一致,说明最开始我们应该也不同向。那么我们就找到了如果要连续的走只能LRLRLRLR…这样走下去。RR和LL都是断点走不通的。

那么我们使用 L i L_i Li数组数组记录第i个位置往左边最远可以走的距离。
使用 t o _ r i to\_r_i to_ri代表这个位置我不能往左边走了,那么既然不能往左边走,那么一定可以从i-1个点往右边来到第i个点,这个数组记录一下每个位置从右边来的最多点数。
同理从后往前处理一下往右边走的最远距离, R i R_i Ri数组记录。
使用 t o _ l i to\_l_i to_li代表第i+1个点通过走左边来到第i个点的最大点数。
那么更新操作也比较简单,从前遍历的时候判断一下是不是L,如果是 L i = t o _ r i − 1 + 1 L_i=to\_r_{i-1}+1 Li=to_ri1+1
否则 t o _ r i = L i − 1 + 1 to\_r_i=L_{i-1}+1 to_ri=Li1+1。既然走不到左边去,那么可以从左边来右边次数+1。
从后往前类似。

const int N = 3e5 + 7;

ll n, m;
char s[N];
int to_l[N], to_r[N];
int l[N], r[N];

void solve() {
	n = read();
	scanf("%s", s + 1);
	rep(i, 0, n + 4)
		l[i] = r[i] = to_l[i] = to_r[i] = 0;
	if (s[1] == 'L')	l[1] = 1;
	else to_r[1] = 1;
	rep(i, 2, n) {
		if (s[i] == 'L')	l[i] = to_r[i - 1] + 1;
		else to_r[i] = l[i - 1] + 1;
	}
	if (s[n] == 'R') r[n] = 1;
	else to_l[n] = 1;
	repp(i, n - 1, 1) {
		if (s[i] == 'R') r[i] = to_l[i + 1] + 1;
		else to_l[i] = r[i + 1] + 1;
	}
	rep(i, 0, n) {
		ll ans = 1;
		if (i != 0)	ans += l[i];
		if (i != n)	ans += r[i + 1];
		print(ans, 32);
	}
	puts("");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值