Educational Codeforces Round 144 (Rated for Div. 2) (A~E) 题解

A. Typical Interview Problem

题意描述

给你一个字符串评判机制,每次询问给个字符串,问是否符合要求。

简要分析

不难发现 FBFFBFFB 是循环节,只需判断 s s s 是否在有上述循环节组成的字符串中即可。

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>

using namespace std;

typedef long long ll;
const ll maxn = 1e5 + 7;
const ll INF = 1e9 + 7, MOD = 998244353;

inline ll read() {
    char cCc;
    ll xXx = 0, wWw = 1;
    while (cCc < '0' || cCc > '9')
        (cCc == '-') && (wWw = -wWw), cCc = getchar();
    while (cCc >= '0' && cCc <= '9')
        xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
    xXx *= wWw;
    return xXx;
}

string t = "FBFFBFFBFBFFBFFBFBFFBFFBFBFFBFFB";

void solve() {
    ll n;
    string s;
    cin >> n >> s;
    if (t.find(s) != string::npos) cout << "Yes" << '\n';
    else cout << "No" << '\n';
}

signed main() {
//    freopen("code.in","r",stdin);
//    freopen("code.out","w",stdout);
    ll T = read();
    while (T--) solve();
    return 0;
}

B. Asterisk-Minor Template

题意描述

给你两个字符串 S , T S,T S,T,你需要给出一个由 * 与小写字母组成的字符串,需要满足以下条件:

  1. 其中个数小于等于小写字母的个数。
  2. 使用若干小写字母代替 * 使得与 S S S 相同,每个 * 可以用不同的若干小写字母代替。
  3. 使用若干小写字母代替 * 使得与 T T T 相同,每个 * 可以用不同的若干小写字母代替。

没有满足条件的输出 NO,否需要先输出 YES。

简要分析

不按发现,需要给出的字符串要么是 X ∗ X* X ∗ X *X X ∗ X ∗ *X* X,其中 X X X 代表若干小写字母,对于第三者 X X X 的长度必须大于等于 2 2 2

随后,分类讨论即可。

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>

using namespace std;

typedef long long ll;
const ll maxn = 1e5 + 7;
const ll INF = 1e9 + 7, MOD = 998244353;

inline ll read() {
    char cCc;
    ll xXx = 0, wWw = 1;
    while (cCc < '0' || cCc > '9')
        (cCc == '-') && (wWw = -wWw), cCc = getchar();
    while (cCc >= '0' && cCc <= '9')
        xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
    xXx *= wWw;
    return xXx;
}

inline void write(ll xXx) {
    if (xXx < 0)
        putchar('-'), xXx = -xXx;
    if (xXx > 9)
        write(xXx / 10);
    putchar(xXx % 10 + '0');
}

void solve() {
    string s, t;
    cin >> s >> t;
    if (s[0] == t[0]) {
        cout << "YES\n";
        cout << s[0] << "*\n";
    } else if (s[s.size() - 1] == t[t.size() - 1]) {
        cout << "YES\n" << "*" << t[t.size() - 1] << '\n';
    } else {
        bool flag = false;
        set<string> v1;
        for (ll i = 0; i + 1 < s.size(); i++) {
            v1.insert(s.substr(i, 2));
        }
        for (ll i = 0; i + 1 < t.size(); i++) {
            if (v1.contains(t.substr(i, 2))) {
                cout << "YES\n" << '*' << t.substr(i, 2) << "*\n";
                flag = true;
                break;
            }
        }
        if(!flag)cout << "NO\n";
    }
}

signed main() {
//    freopen("code.in","r",stdin);
//    freopen("code.out","w",stdout);
    ll T = read();
    while (T--)solve();
    return 0;
}

C. Maximum Set

题意描述

给你一个区间,求一个集合 S , S ∈ [ l , r ] S,S\in[l,r] S,S[l,r] 的最大长度与最长集合的个数。

需要满足 ∀ i , j ∈ S ,    i ∣ j  或  j ∣ i \forall i,j \in S , \ \ i | j \ 或 \ j | i i,jS,  ij  ji,集合长度为 1 1 1 默认合法。

简要分析

不难发现,最长长度的集合长这样 l , l × 2 , l × 2 2 , . . . , l × 2 x l ,l\times 2,l\times 2^2 , ...,l \times2^x l,l×2,l×22,...,l×2x

那么我们就可以处理出集合的最长长度。

接下来考虑仅 × 2 \times 2 ×2 时的集合数量。

显然,我们只需将 r r r 除以 x − 1 x - 1 x1 2 2 2 就可以知道集合的初值所属于的最大区间,即求出了集合数量。

我们考虑到,可以将集合中的某一个 × 2 \times 2 ×2 替换成 × 3 \times3 ×3

如果替换两个即等价于 × 9 \times 9 ×9,这时不妨使用 3 3 3 × 2 \times 2 ×2 即为 × 8 \times 8 ×8 得到更优答案。

对于计算存在一个 × 3 \times 3 ×3 的集合个数,我们如法炮制。

r r r 除以 x − 1 x-1 x1 2 2 2 随后再除以 3 3 3 就可以知道集合的初值所属于的最大区间,即求出了集合数量。

时间复杂度 O ( T log ⁡ r ) O(T\log r) O(Tlogr)

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>

using namespace std;

typedef long long ll;
const ll maxn = 1e5 + 7;
const ll INF = 1e9 + 7, MOD = 998244353;

inline ll read() {
    char cCc;
    ll xXx = 0, wWw = 1;
    while (cCc < '0' || cCc > '9')
        (cCc == '-') && (wWw = -wWw), cCc = getchar();
    while (cCc >= '0' && cCc <= '9')
        xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
    xXx *= wWw;
    return xXx;
}

inline void write(ll xXx) {
    if (xXx < 0)
        putchar('-'), xXx = -xXx;
    if (xXx > 9)
        write(xXx / 10);
    putchar(xXx % 10 + '0');
}

void solve() {
    ll n = read(), m = read();
    ll tmp = m, ans = 0, cnt = 0, lim;
    if (n * 2 > m) {
        cout << 1 << " " << m - n + 1 << '\n';
        return;
    }
    while (tmp >= n) cnt++, tmp /= 2;
    lim = cnt - 1;
    tmp = m;

    while (lim--) tmp /= 2;

    ans = tmp - n + 1;
    lim = cnt - 2;
    tmp = m;
    while (lim--) tmp /= 2;
    ans += max(0ll, (tmp / 3 - n + 1) * (cnt - 1));
    cout << cnt << ' ' << ans << endl;
}

signed main() {
//    freopen("code.in","r",stdin);
//    freopen("code.out","w",stdout);
    ll T = read();
    while (T--)solve();
    return 0;
}

D. Maximum Subarray

题意描述

简要分析

观察到要求最大子段和,但发现需要对数组进行操作,于是考虑如何改编最大字段和。

这里我们改变使用分治(线段树)的方法求解。

关于如何使用分治(线段树)的方法求解最大子段和这里不再赘述。

观察到在建树时递归到最底端,也就是 l = = r l == r l==r 是,此时维护的最大字段和、以左端点为首元素的最大子段和,以右端点为尾元素的最大子段和均等于 a l a_l al a r a_r ar

我们可以在这里分类讨论,如果 k = 0 k = 0 k=0 那么只需将 a l − x a_l - x alx a r − x a_r - x arx 即可。

k ≠ 0 k \not= 0 k=0 要么将 a l + x a_l + x al+x a r + x a_r + x ar+x 要么将 a l − x a_l - x alx a r − x a_r -x arx

我们只需讲这两种情况均与另一节点合并至父节点中即可。

合并时仅需枚举所有配对情况取最大值即可。

又因为此题仅需一次建树一次查询所以可以省去许多冗余的函数,代码十分简洁。

时间复杂度为 O ( n k ) O(nk) O(nk)

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>

using namespace std;

typedef long long ll;
const ll maxn = 2e5 + 7;
const ll INF = 1e18 + 7, MOD = 998244353;

inline ll read() {
    char cCc;
    ll xXx = 0, wWw = 1;
    while (cCc < '0' || cCc > '9')
        (cCc == '-') && (wWw = -wWw), cCc = getchar();
    while (cCc >= '0' && cCc <= '9')
        xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
    xXx *= wWw;
    return xXx;
}

inline void write(ll xXx) {
    if (xXx < 0)
        putchar('-'), xXx = -xXx;
    if (xXx > 9)
        write(xXx / 10);
    putchar(xXx % 10 + '0');
}

struct node {
    ll sum, lq, rq, qs;

    node() {
        sum = -INF, lq = -INF, rq = -INF, qs = -INF;
    }

    node(ll sum, ll lq, ll rq, ll qs) : sum(sum), lq(lq), rq(rq), qs(qs) {}
};

ll n, k, x, a[maxn];

vector<node> solve(ll l, ll r) {
    if (l == r) {
        ll s1 = max(0ll, a[r] + x);
        ll s2 = max(0ll, a[r] - x);
        if (k == 0) return {node(a[r] - x, s2, s2, s2)};
        return {node(a[r] - x, s2, s2, s2), node(a[r] + x, s1, s1, s1)};
    }
    ll mid = (l + r) / 2;
    auto vl = solve(l, mid), vr = solve(mid + 1, r);
    ll len = min(k, r - l + 1) + 1;
    vector<node> ans(len);
    for (ll i = 0; i < vl.size(); i++) {
        for (ll j = 0; j < vr.size() && i + j < len; j++) {
            auto &l = vl[i], &r = vr[j], &u = ans[i + j];
            u.sum = max(u.sum, l.sum + r.sum);
            u.lq = max(u.lq, l.lq);
            u.lq = max(u.lq, l.sum + r.lq);
            u.rq = max(u.rq, r.rq);
            u.rq = max(u.rq, l.rq + r.sum);
            u.qs = max(u.qs, l.qs);
            u.qs = max(u.qs, r.qs);
            u.qs = max(u.qs, l.rq + r.lq);
        }
    }
    return ans;
};

signed main() {
    ll T = read();
    while (T--) {
        n = read(), k = read(), x = read();
        for (ll i = 1; i <= n; i++) a[i] = read();
        cout << solve(1, n)[k].qs << '\n';
    }
}

E. Colored Subgraphs

题目大意

给你一棵树, n n n 个节点。你需要给每个节点染色,使得任意颜色相同的节点之间的路径上的点颜色也与前面两个点相同。

c n t i cnt_i cnti 表示颜色为 i i i 的节点数,找到 min ⁡ c n t i \min cnt_i mincnti 的最大值。

简要分析

先不考虑,根不固定的情况,强制钦定 r = 1 r = 1 r=1,这里 r r r 指根节点。

我们发现答案通过二分可以转化成判定性问题,即能否将树划分成若干条自底向上目长度至少为 k k k 的链。

d e p u dep_u depu 表示,自底向上的到达 u u u 的最短长度, m i n x minx minx 表示么 x x x 的儿子中 d e p v dep_v depv 的最小值。

显然对于叶子有, d e p w = 1 dep_w=1 depw=1,而对于非时子节点,显然有转移 d e p w = m i n x + 1 dep_w=minx +1 depw=minx+1

对于不合法的情况, u u u 存在两个儿子,他们的 d e p w dep_w depw 都要小于 k k k,因为 u u u 只能和一个儿子往上拼接,两个就不行了,这个记为情況 1 1 1

最终只需要判断一下 d e p 1 dep_1 dep1, 是否不小于 k k k 即可,这个记为情况 2 2 2

然后就是, r r r 不固定的情况,当然,假设说 r = 1 r = 1 r=1 可行那么我们就直接输出了。

如果情况 1 1 1 不合法,子树以外的点作为根是不可能的,因为这个子树的本身结构是不变的,所以我们就需要将 m i n x minx minx 对应的节点 v v v 变成根。

因为这种情况不合法一定是说明了这个点到 u u u 距离是小于 k k k 的,而肯定还有一个 v ’ v’ v u u u 的距离是小于 v v v 的,所以我们本质上是把 v v v v ’ v’ v 的路径涂成一种颜色。

如果情况 2 2 2 不合法,那么我们同理,也是找 m i n x minx minx 就行了。

总时间复杂度 O ( n ) O(n) O(n)

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>

using namespace std;

typedef long long ll;
const ll maxn = 2e5 + 7;
const ll INF = 1e18 + 7, MOD = 998244353;

inline ll read() {
    char cCc;
    ll xXx = 0, wWw = 1;
    while (cCc < '0' || cCc > '9')
        (cCc == '-') && (wWw = -wWw), cCc = getchar();
    while (cCc >= '0' && cCc <= '9')
        xXx = (xXx << 1) + (xXx << 3) + (cCc ^ '0'), cCc = getchar();
    xXx *= wWw;
    return xXx;
}

inline void write(ll xXx) {
    if (xXx < 0)
        putchar('-'), xXx = -xXx;
    if (xXx > 9)
        write(xXx / 10);
    putchar(xXx % 10 + '0');
}

ll n, tx, ty, dep[maxn], minnode[maxn];
vector<ll> G[maxn];

bool dfs(ll u, ll fa, ll k) {
    ll x = 0, y = 0, miny = INF, minx = INF;
    for (auto v : G[u]) {
        if (v == fa)continue;
        if (!dfs(v, u, k))return false;
        if (dep[v] <= minx)y = x, miny = minx, x = minnode[v], minx = dep[v];
        else if (dep[v] <= miny)y = minnode[v], miny = dep[v];
    }

    if (miny < k) {
        tx = x, ty = y;
        return false;
    }

    if (x == 0)dep[u] = 1, minnode[u] = u;
    else dep[u] = minx + 1, minnode[u] = x;

    if (!fa) {
        if (dep[u] < k)tx = minnode[u], ty = 1;
        return dep[u] >= k;
    }

    return true;
}

bool check(ll k) {
    if (dfs(1, 0, k)) return true;
    else if (dfs(tx, 0, k) || dfs(ty, 0, k))return true;
    return false;
}

void solve() {
    n = read();
    for (ll i = 1; i < n; i++) {
        ll u = read(), v = read();
        G[u].push_back(v);
        G[v].push_back(u);
    }

    ll l = 0, r = n, ans = 0;
    while (l <= r) {
        ll mid = (l + r) >> 1;
        if (check(mid))
            ans = mid, l = mid + 1;
        else r = mid - 1;
    }

    cout << ans << '\n';
    for (ll i = 1; i <= n; i++)G[i].clear();
}

signed main() {
//    freopen("code.in","r",stdin);
//    freopen("code.out","w",stdout);
    ll T = read();
    while (T--)solve();
    return 0;
}

总结

赛时慎用模版,听说有好多人使用同个模版,近似逐字符相同,然后就无了。

—— C l a u d e Z . \mathscr{ClaudeZ.} ClaudeZ.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"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、付费专栏及课程。

余额充值