Educational Codeforces Round 103 题解报告A-D
A. K-divisible Sum
题目大意:
给定 n 个数和一个参数 k ,并构造一个数列 a1an~ 的和能够被给定参数 k 整除,问这样满足条件的数列中的最大值最小是多少
我们考虑一下两种情况:
- 给定的 n ≤ k n \le k n≤k 的时候,我们一定可以通过取平均值并对平均值向上取整得到答案
- 给定的 $n > k $ 的时候,这时我们再用 k 直接去分配就不够了,于是我们需要找到第一个大于 n 的 k 的倍数,然后进行上述操作
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, m;
scanf("%d%d", &n, &m);
if (m < n)
{
if (n % m == 0)
m = n;
else
m = (n / m * m) + m;
}
printf("%.0f\n", ceil((m * 1.0) / n));
}
return 0;
}
B. Inflation
题目大意:
给定序列 {p} ,最少需要加多少,使得对于任意
p
i
+
1
∑
i
=
1
n
−
1
p
i
≤
k
100
\frac{p_{i+1}}{\sum_{i=1}^{n-1}p_i} \le \frac{k}{100}
∑i=1n−1pipi+1≤100k
分析可知,我们对任意
i
∈
[
2
,
n
]
i\in[2,n]
i∈[2,n] ,
a
i
+
x
a_i+x
ai+x 可以完全等效于
a
1
+
x
a_1+x
a1+x ,于是我们有一下两种思路:
- 遇到不满足上述条件的,我们直接在当前点修改,并将当前加上的值看做是加在 a 1 a_1 a1 上的
- 我们直接通过二分,判断$ q_1 $ 加上某个数后是否满足条件,如果满足,缩小范围,否者扩大范围
//直接扫描数组
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 1000010;
int n, k;
ll a[MAX], sum;
inline ll check(ll fz)
{
ll fm = (100 * fz + k - 1) / k;
return fm;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
ll ans = 0;
sum = a[1];
for (int i = 2; i <= n; i++)
{
if (a[i] * 100 > sum * k)
{
ll temp = check(a[i]);
ans += temp - sum;
sum = temp;
}
sum += a[i];
}
printf("%lld\n", ans);
}
return 0;
}
//二分法
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 10010;
int n, k;
ll a[MAX], b[MAX];
inline bool check(ll mid)
{
b[1] = a[1] + mid;
for (int i = 2; i <= n; i++)
{
b[i] = b[i - 1] + a[i];
if (100 * a[i] > k * b[i - 1])
return false;
}
return true;
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
ll l = 0, r = 1e12;
while (l < r)
{
ll mid = (l + r) / 2;
if (check(mid))
r = mid;
else
l = mid + 1;
}
printf("%lld\n", l);
}
return 0;
}
C. Longest Simple Cycle
题目大意:
给出 n 条链,每条链上按顺序分布着 ci 个点,序号从1-ci ,并且给出 2n 个链接,使除1号链外所有的链,都和 i-1 号链相连,具体操作如下
- 第一条链被跳过
- 第 i 条链第 1 个顶点与第 (i-1) 条链的第 ai 号顶点通过一条边连接
- 第 i 链的最后一个顶点与 (i-1) 条链的 bi 号顶点通过一条边连接
问这样构成的一个最大环
思路分析:
我们构造一个一个前缀和数组,这个数组记录的是:
对于第i条链,前 i-1条链在向第 i 条链转移时的最多点个数
那么我们容易得到以下动态规划方程
a
n
s
=
m
a
x
(
a
n
s
,
s
u
m
[
i
]
+
c
[
i
]
)
ans=max(ans,sum[i]+c[i])
ans=max(ans,sum[i]+c[i])
其中 sum[i] 表示前 i-1 条链最多有多少能够向第 i 条链转移,c[i] 即题目中 c[i]
现在我们只需要求出 sum[i] 即可
对于 sum[i] 我们做出如下三种分类:
- i=2 时,我们直接计算第一条链即可
- a[i]=b[i] 时,这时由于这个点被重复走,所以必定清空前面所有的贡献,新的贡献仅有 1 个点
- 非以上两种情况时,sum[i] 的值就是 sum[i-1] 的值加上在 i-1 条链上能走的距离
//开long long
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int MAX = 200010;
ll a[MAX], b[MAX], c[MAX];
ll sum[MAX];
int n;
signed main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &c[i]), sum[i] = 0;
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%lld", &b[i]);
ll ans = 0;
for (int i = 2; i <= n; i++)
{
if (a[i] > b[i])
swap(a[i], b[i]);
if (i == 2)
sum[i] = b[i] - a[i] + 1;
else if (a[i] == b[i])
sum[i] = 1;
else
sum[i] = max(sum[i - 1] + (c[i - 1] - b[i] + 1) + (a[i]), b[i] - a[i] + 1);
ans = max(ans, sum[i] + c[i]);
}
printf("%lld\n", ans);
}
return 0;
}
D. Journey
题目大意:
一条链上分布有 0~n 个点,每一个点中间有一条单向边连接,并用字母 ‘L’ 和 ‘R’表示
若为 L 说明该边方向向左,若为 R 说明该边方向向右
你可以在任意一点开始移动,并且每移动一步,上述边的方向改变一次
要求求出每一个点能够到达的最多点数(若一个点被多次走到,那么仅算一次)
思路分析:
显然,当某一个点左边 RL 交替出现或其右边 LR 交替出现,其能够一直向下走下去
并且,当这种交替状态走到尽头时,一定能够原路返回
那么我们只要求出一个点的左边有多少 L、R交替出现(无顺序)和右边L、R交替出现(无顺序)的最大长度,最终统计答案输出即可
特别的,由于首尾都只能单方向行走,所以需要特判
#include <bits/stdc++.h>
using namespace std;
const int MAX = 300010;
int n, sum1[MAX], sum2[MAX];
char s[MAX];
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
scanf("%d%s", &n, s + 1);
for (int i = 1; i <= n; i++)
sum1[i] = sum2[i] = 0;
//预处理
s[0] = s[1];
s[n + 1] = s[n];
for (int i = 1, temp = 1; i <= n; i++)
{
if (s[i] != s[i - 1])
sum1[i] = ++temp;
else
sum1[i] = temp = 1;
}
for (int i = n, temp = 1; i >= 1; i--)
{
if (s[i] != s[i + 1])
sum2[i] = ++temp;
else
sum2[i] = temp = 1;
}
//输出
//第一个特判
if (s[1] == 'R')
printf("%d ", sum2[1] + 1);
else
printf("1 ");
for (int i = 1; i < n; i++)
{
int sum = 1;
if (s[i] == 'L') //当前点能够向左边走
sum += sum1[i];
if (s[i + 1] == 'R') //当前点能够向右边走
sum += sum2[i + 1];
printf("%d ", sum);
}
//最后一个特判
if (s[n] == 'L')
printf("%d\n", sum1[n] + 1);
else
printf("1\n");
}
return 0;
}