Educational Codeforces Round 167 (Rated for Div. 2)


比赛链接:https://codeforces.com/contest/1989

A. Catch the Coin

题意

n n n 个硬币坐标 ( x i , y i ) (x_i,y_i) (xi,yi),起始位置为 ( 0 , 0 ) (0,0) (0,0),每次行动可以向上下左右和斜向的八个方向移动一步,行动一次后,硬币会下移一格,即从 ( x i , y i ) (x_i,y_i) (xi,yi) 移动到 ( x i , y i − 1 ) (x_i,y_i-1) (xi,yi1)
对于每个硬币,确认是否能走到该硬币中的位置从而捡起硬币。

数据范围

n n n ( 1 ≤ n ≤ 500 1 \le n \le 500 1n500), x i x_i xi and y i y_i yi ( − 50 ≤ x i , y i ≤ 50 -50 \le x_i, y_i \le 50 50xi,yi50)

思路

显然最优的策略是走到硬币的正下方位置,因为硬币会一直下移,所以如果能走到硬币的正下方位置一定能走到硬币的位置。

复杂度

时间复杂度为 O ( ∣ x i ∣ ) O(|x_i|) O(xi)

代码实现

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

#define int long long

void solve()
{
    int x, y;
    cin >> x >> y;

    int dx = 0;
    // dx为x轴偏移量
    if (x > 0)
        dx = 1;
    else if (x < 0)
        dx = -1;

    int xi = 0, yi = 0, f = 0;
    // (xi,yi) 为当前走到位置,起始为 (0,0)
    do {
        if (f)
            y--;
        f = 1;
        // 要注意的是,是移动一步之后,硬币才下移一格,所以要用变量f标记一下此时是否走了一步
        if (xi != x)
            xi += dx;
        yi--;
    } while (xi != x);

    // 判断是否是在正下方(包含相等的情况)
    if (yi <= y)
        cout << "YES\n";
    else
        cout << "NO\n";
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T;
    cin >> T;
    while (T--) {
        solve();
    }
}

B. Substring and Subsequence

题意

给两个字符串 a a a b b b,计算包含 a a a 为子串 和 把 b b b 作为子序列的字符串的最小长度。

数据范围

a a a ( 1 ≤ ∣ a ∣ ≤ 100 1 \le |a| \le 100 1a100), b b b ( 1 ≤ ∣ b ∣ ≤ 100 1 \le |b| \le 100 1b100),

思路

令构造的字符串为 s s s
因为 a a a s s s 的子串,所以 b b b 要么会有部分存在在 a a a 中, 要么就没有。
1,如果 b b b 没有部分存在 a a a 中,那么最优的方法就是令 s = a + b s = a+b s=a+b,此时 s s s 长度就是 ∣ a ∣ + ∣ b ∣ |a| + |b| a+b
2,如果 b b b 有部分存在 a a a 中,若这部分长度为 x x x,那么 s s s 的最小长度为 ∣ a ∣ + ∣ b ∣ − x |a| + |b| - x a+bx
因为字符串长度不大,所以可以枚举 b b b 中每一位字母作为 ∣ a ∣ |a| a 中存在部分的开头,然后进行匹配,求出这一位作为开头时能在 ∣ a ∣ |a| a 中部分的最长长度 x x x
上述两种情况的较小值即为答案。

复杂度

时间复杂度为 O ( ∣ a ∣ ∗ ∣ b ∣ ) O(|a|*|b|) O(ab)

代码实现

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

#define int long long

void solve()
{
    string sa, sb;
    cin >> sa >> sb;

    int n1 = sa.size(), n2 = sb.size(), ans = n1 + n2;

    for (int i = 0; i < n2; i++) {
        int j = i;
        // j为从i开始匹配的最大下标,初始为i
        for (int k = 0; k < n1; k++) {
            if (j < n2 && sa[k] == sb[j])
                j++;
            // 每找到字符串sa中有一位和当前sb[j]相同,j后移一位
        }
        int cur = n1 + n2 - (j - i);
        // j-i即为匹配的最大部分长度
        ans = min(ans, cur);
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T;
    cin >> T;
    while (T--) {
        solve();
    }
}

C. Two Movies

题意

有两部电影,有 n n n 个人,第 i i i 个人对第一个电影的评价值为 a i a_i ai,对第二个电影的评价值为 b i b_i bi
每个人可以选择其中一部电影留下评论,电影的评分为留下评论的评价值总和,公司的评分是两部电影评分的最小值,求公司可能获得的最大评分。

数据范围

n n n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2 \cdot 10^5 1n2105), − 1 ≤ a i , b i ≤ 1 -1 \le a_i,b_i \le 1 1ai,bi1

思路

对于第 i i i 个人的评价 ( a i , b i ) (a_i,b_i) (ai,bi) 进行分类讨论:
1,如果 a i = 0 , b i = 0 a_i = 0,b_i =0 ai=0,bi=0,不会造成任何贡献,可忽略。
2,如果 a i = 0 / − 1 , b i = 1 a_i = 0/-1,b_i = 1 ai=0/1,bi=1,显然让第 i i i 个人评论第二步电影是最优的,因为评价第一部电影只会让最小值不变或者更小,评价第二部电影则会让最小值不变或者增大。(把 a i , b i a_i,b_i ai,bi 的数值反过来也是同理)。
因为这种情况是可以直接确定要评价哪一个的,所以可以先处理这种情况,然后得到两部电影的得分 s a , s b sa,sb sa,sb
3,如果 a i = 1 , b i = 1 a_i=1,b_i =1 ai=1,bi=1,对于这种情况,选择加在 s a , s b sa,sb sa,sb 中的较小值是最优的,因为这会使得最小值增大。
同样的,也是把这种情况全部处理后再处理下一种情况。
4,如果 a i = − 1 , b i = − 1 a_i=-1,b_i=-1 ai=1,bi=1,对于这种情况,选择减再 s a , s b sa,sb sa,sb 中的较大值是最优的。
处理完上述所有情况之后, m i n ( s a , s b ) min(sa,sb) min(sa,sb) 即为答案。

代码实现

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

#define int long long

const int N = 2e5 + 5;

int n;
int a[N], b[N];

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int c1 = 0, c2 = 0, sa = 0, sb = 0;
    // c1记录(-1,-1)的数量
    // c2记录(1,1)的数量
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        if (a[i] == -1 && b[i] == -1) {
            c1++;
        } else if (a[i] == 1 && b[i] == 1) {
            c2++;
        } else {
            // 经过前面两个判断,如果 a[i]=b[i],只有(0,0)的情况,不影响
            // 否则就是(1,0/-1) 或者 (0/-1,1)的情况,往1对应的分数加即可
            a[i] >= b[i] ? sa += a[i] : sb += b[i];
        }
    }
    while (c2--)
        sa <= sb ? sa++ : sb++;
    while (c1--)
        sa >= sb ? sa-- : sb--;

    cout << min(sa, sb) << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T;
    cin >> T;
    while (T--) {
        solve();
    }
}

D. Smithing Skill

题意

n n n 个技能, m m m 种金属。
1,第 i i i 种金属有 c i c_i ci 个。
2,第 i i i 个技能可以花费 a i a_i ai 个任意种金属制造第 i i i 种武器(制造武器只能用一种金属,不过可以任意选择种类),第 i i i 种武器熔化后可以返还 b i b_i bi 个制造时用的对应种类金属。
制造和熔化都能获得一点经验值,问能得到的最大经验值是多少?

数据范围

1 ≤ n , m ≤ 1 0 6 1 \le n, m \le 10^6 1n,m106 1 ≤ a i ≤ 1 0 6 1 \le a_i \le 10^6 1ai106 0 ≤ b i < a i 0 \le b_i < a_i 0bi<ai 1 ≤ c j ≤ 1 0 9 1 \le c_j \le 10^9 1cj109

思路

因为制造武器只能用一种金属,所以对于每种金属,只需要考虑数量和经验值的关系。

f ( i ) f(i) f(i) 为金属数量为 i i i 时能获得的最大经验值。
因为数量为 i − 1 i-1 i1 能取到经验值 x x x,数量加一也不影响能取得的经验值,所以 f ( i ) f(i) f(i) 可以从 f ( i − 1 ) f(i-1) f(i1) 转移过来。
对于每件武器可以制造完后就熔化,得到 2 2 2 点经验值,消耗的金属数为 a i − b i a_i - b_i aibi,且要求金属制造前的数量不小于 a i a_i ai,因此 f ( i ) f(i) f(i) 可以从 f ( i − ( a j − b j ) ) f(i-(a_j-b_j)) f(i(ajbj)) 转移过来,且经验值加 2 2 2
注意到, f ( i ) f(i) f(i) 是单调不减的,即 i i i 越大, f ( i ) f(i) f(i) 越大,所以 a j − b j a_j-b_j ajbj 可以取最小值,从 f ( i − m i n ( a j − b j ) ) f(i-min(a_j-b_j)) f(imin(ajbj)) 转移过来。
因此, f ( i ) = m a x ( f ( i − 1 ) , f ( i − m i n ( a j − b j ) ) + 2 ) ) f(i) = max(f(i-1),f(i-min(a_j-b_j))+2)) f(i)=max(f(i1),f(imin(ajbj))+2))

但是,观察数据范围,发现 i i i 的上界是 1 0 9 10^9 109,貌似是没办法递推,但是 a i a_i ai 的上界是 1 0 6 10^6 106,意味着当 i > 1 0 6 i>10^6 i>106 时,可以取得 n n n 种武器中消耗最小的进行消耗,即选择最小的 a i − b i a_i-b_i aibi 进行减少,每次减少增加 2 2 2 点经验值,直到减少到不大于 1 0 6 10^6 106,然后直接通过 f ( i ) f(i) f(i) 计算得到最大值。
因此,令 i 1 i_1 i1 i i i 减到不大于 1 0 6 10^6 106 时的数值,则 f ( i ) = ⌈ i − 1 0 6 m i n ( a i − b i ) ⌉ ∗ 2 + f ( i 1 ) f(i) = \lceil \frac{i-10^6}{min(a_i-b_i)}\rceil*2+f(i_1) f(i)=min(aibi)i1062+f(i1)

此时需要考虑的问题就是,要怎么快速求出当金属数量为 x x x 时,能取到的最小的 a i − b i a_i-b_i aibi ?显然,直接遍历去计算复杂度是 O ( n ∗ m ) O(n*m) O(nm) 会超时。
d ( i ) d(i) d(i) 为金属数量为 i i i 是能取到的最小 a i − b i a_i-b_i aibi
注意到,金属数量 i i i 和取到的最小 a i − b i a_i-b_i aibi 成单调不增的关系,所以和求 f ( i ) f(i) f(i) 类似, d ( i ) d(i) d(i) 可以从 d ( i − 1 ) d(i-1) d(i1) 转移而来,且对于第 i i i 种武器,可以直接使 d ( a i ) d(a_i) d(ai) a i − b i a_i -b_i aibi 比较取最小值。
综上, d ( i ) = m i n ( d ( i − 1 ) , a i − b i ) ( a i = i 且 1 ≤ i ≤ n ) d(i) = min(d(i-1),a_i-b_i)(a_i=i 且 1\le i \le n) d(i)=min(d(i1),aibi)(ai=i1in),则 f ( i ) = m a x ( f ( i − 1 ) , f ( i − d ( i ) ) + 2 ) f(i) = max(f(i-1),f(i-d(i))+2) f(i)=max(f(i1),f(id(i))+2)

最后的答案为所有金属能获得的最大经验值的总和,即 ∑ i = 1 m f ( c i ) \sum^m_{i=1}f(c_i) i=1mf(ci)

代码实现

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

#define int long long

const int N = 1e6 + 5;

int n, m, k;
int a[N], b[N], c[N], d[N], f[N];

void solve()
{
    cin >> n >> m;
    memset(d, 0x3f, sizeof(d));
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        d[a[i]] = min(d[a[i]], a[i] - b[i]);
    }
    for (int i = 1; i < N; i++) {
        d[i] = min(d[i], d[i - 1]);
    }
    for (int i = 1; i < N; i++) {
        f[i] = f[i - 1];
        if (i - d[i] >= 0) {
            f[i] = max(f[i], f[i - d[i]] + 2);
        }
    }
    int ans = 0, del = d[N - 1];
    // d[i] 为金属数量为i时取得的最小消耗 a[j]-b[j]
    // d[N-1] 即为 n种武器能取得的最小消耗
    for (int i = 1; i <= m; i++) {
        cin >> c[i];
        // 不一定是减到1e6以内,只要减到定义的上界N以内即可,N>=1e6
        if (c[i] >= N) {
            int k = c[i] - (N - 1);
            int v = (k + del - 1) / del;
            // v为减到N以内的操作次数
            ans += 2 * v;
            c[i] -= del * v;
        }
        ans += f[c[i]];
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    int T = 1;
    // cin >> T;
    while (T--) {
        solve();
    }
}
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值