Educational Codeforces Round 103 题解报告A-D

A. K-divisible Sum

题目大意:

给定 n 个数和一个参数 k ,并构造一个数列 a1an~ 的和能够被给定参数 k 整除,问这样满足条件的数列中的最大值最小是多少

我们考虑一下两种情况:

  1. 给定的 n ≤ k n \le k nk 的时候,我们一定可以通过取平均值并对平均值向上取整得到答案
  2. 给定的 $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=1n1pipi+1100k
分析可知,我们对任意 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 ,于是我们有一下两种思路:

  1. 遇到不满足上述条件的,我们直接在当前点修改,并将当前加上的值看做是加在 a 1 a_1 a1 上的
  2. 我们直接通过二分,判断$ 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 号链相连,具体操作如下

  1. 第一条链被跳过
  2. 第 i 条链第 1 个顶点与第 (i-1) 条链的第 ai 号顶点通过一条边连接
  3. 第 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] 我们做出如下三种分类:

  1. i=2 时,我们直接计算第一条链即可
  2. a[i]=b[i] 时,这时由于这个点被重复走,所以必定清空前面所有的贡献,新的贡献仅有 1 个点
  3. 非以上两种情况时,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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值