【更新完毕】2024牛客寒假算法基础集训营5 题解 | JorbanS

A - mutsumi的质数合数

除去 1 1 1 的数均计入

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

int solve() {
    cin >> n;
    int c1 = 0;
    for (int i = 0; i < n; i ++) cin >> a[i], c1 += a[i] == 1;
    return n - c1;
}

B - tomorin的字符串迷茫值

因为不能删除连续的俩字符,故只有八种子串通过操作能够变成 m y g o mygo mygo

mygo, m_ygo, my_go, myg_o, m_y_go, m_yg_o, my_g_o, m_y_g_o

匹配子串,子串内部删除一定,只要看子串左右两侧

可以发现对于长度为 i i i 的串,删除方式数为斐波那契数列

f [ 0 ] = 1 , f [ 1 ] = 2 f [ i ] = f [ i − 1 ] + f [ i − 2 ] ,   x ≥ 3 f[0]=1,f[1]=2\\f[i]=f[i-1]+f[i-2],~x\ge3 f[0]=1,f[1]=2f[i]=f[i1]+f[i2], x3

即对于当前字符能删,则从 f [ i − 2 ] f[i-2] f[i2] 转移而来

不删,则由 f [ i − 1 ] f[i-1] f[i1] 转移而来

int f[N];

int solve() {
    cin >> s;
    n = s.size();
    f[0] = 1, f[1] = 2;
    for (int i = 2; i <= n; i ++) f[i] = ((ll)f[i - 1] + f[i - 2]) % mod;
    int res = 0;
    for (int i = 0; i < n; i ++)
        if (s[i] == 'm')
            for (int j = i + 1; j <= i + 2; j ++)
                if (s[j] == 'y')
                    for (int k = j + 1; k <= j + 2; k ++)
                        if (s[k] == 'g')
                            for (int l = k + 1; l <= k + 2; l ++)
                                if (s[l] == 'o')
                                    res = ((ll)res + (ll)f[i] * f[n - 1 - l] % mod) % mod;
    return res;
}

C - anon的私货

贪心的加入 0 0 0,使得一个数两侧的 0 0 0 的个数小于该数

注意头尾边界

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

ll solve() {
    cin >> n;
    for (int i = 0; i < n; i ++) cin >> a[i];
    a[n] = 2e9;
    ll res = a[0] - 1;
    int t = res;
    for (int i = 0; i < n; i ++) {
        int x = min(a[i] - t, a[i + 1]) - 1;
        res += x;
        t = x;
    }
    return res; 
}

E - soyorin的数组操作(easy)

n n n 为偶数时,只要选定 k = n k=n k=n 进行有限次操作,最终肯定符合非降序

n n n 为奇数时,因为只能操作偶数位,而末尾为奇数位,因此应当尽可能让后面的数更大,但不超过这个数后面的数

尽可能往上加,因为进行该操作不会影响该位置后面的数,也不会让该数前面的数更不符合条件(相对地)

操作的本质是区间相邻两数之差加一

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

string solve() {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    if (n & 1 ^ 1) return yes;
    ll cnt = 0;
    for (int i = n - 1; i >= 1; i --) {
        a[i + 1] += cnt;
        if (a[i] > a[i + 1]) return no;
        if (i & 1 ^ 1) cnt += (a[i + 1] - a[i]) / i;
    }
    return yes;
}

F - soyorin的数组操作(hard)

在贪心可以的前提下,答案为最大的 a i − a i + 1 a_i-a_{i+1} aiai+1

ll solve() {
    cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    ll cnt = 0, res = 0;
    for (int i = n - 1; i >= 1; i --) {
        res = max(res, a[i] - a[i + 1]);
        if (n & 1 ^ 1) continue;
        a[i + 1] += cnt;
        if (a[i] > a[i + 1]) return -1;
        if (i & 1 ^ 1) cnt += (a[i + 1] - a[i]) / i;
    }
    return res;
}

G - sakiko的排列构造(easy)

本题也可以使用暴力 O ( n 2 ) O(n^2) O(n2) 的筛法, c o d e code code 部分为欧拉筛 O ( n ) O(n) O(n)

构造方法为找到比当前剩余 1 ∼ n 1\sim n 1n 数还大的第一个质数 p p p,则可以有一段连续的序列 p − n ,   p − n + 1 ,   ⋅ ⋅ ⋅ ,   n − 1 ,   n p-n,~p-n+1,~···,~n-1,~n pn, pn+1, ⋅⋅⋅, n1, n,保证长度为偶数,因此可以两两相加,它们的和均为 p p p,然后更新 n : = p − n − 1 n:=p-n-1 n:=pn1

如果最后剩余一个数 1 1 1,因为 1 + 1 = 2 1+1=2 1+1=2 也为偶数,因此可以保证上述做法是正确的,也得出结论不存在无解情况

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

int cnt, p[N];
bool vis[N];

void getPrimes(int n = 2e3) {
    for (int i = 2; i <= n; i ++) {
        if (!vis[i]) p[cnt ++] = i;
        for (int j = 0; p[j] <= n / i; j ++) {
            vis[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
}

void solve() {
    cin >> n;
    m = n;
    while (n >= 1) {
        int pr = *upper_bound(p, p + cnt, n);
        for (int i = pr - n; i <= n; i ++) a[i] = pr - i;
        n = pr - n - 1;
    }
    for (int i = 1; i <= m; i ++) cout << a[i] << ' ';
    cout << endl;
}

H - sakiko的排列构造(hard)

本题 n = 1 0 6 n=10^6 n=106 仅可使用欧拉筛,构造方法同 G G G,依然不存在无解情况

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

int cnt, p[N];
bool vis[N];

void getPrimes(int n = 2e6) {
    for (int i = 2; i <= n; i ++) {
        if (!vis[i]) p[cnt ++] = i;
        for (int j = 0; p[j] <= n / i; j ++) {
            vis[i * p[j]] = true;
            if (i % p[j] == 0) break;
        }
    }
}

void solve() {
    cin >> n;
    m = n;
    while (n >= 1) {
        int pr = *upper_bound(p, p + cnt, n);
        for (int i = pr - n; i <= n; i ++) a[i] = pr - i;
        n = pr - n - 1;
    }
    for (int i = 1; i <= m; i ++) cout << a[i] << ' ';
    cout << endl;
}

I - rikki的最短路

判断 T ,   A ,  原点  O T,~A,~原点~O T, A, 原点 O 的相对关系

时间复杂度 O ( 1 ) O(1) O(1)

ll solve() {
    cin >> t >> a >> k;
    if (0 <= a && a <= t || t <= a && a <= 0) return abs(t);
    if (0 <= t && t <= a || a <= t && t <= 0) return abs(a) + abs(t - a);
    if (a >= -k && a <= k) return abs(a) + abs(t - a);
    return abs(t) + 2 * abs(t - a);
}

J - rikki的数组陡峭值

最开始一段区间合并,只剩一个数为止。后面只需要贪心的取最小变化即可

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

ll solve() {
    cin >> n;
    for (int i = 0; i < n; i ++) cin >> a[i] >> b[i];
    int l = 1, r = 1e9;
    ll res = 0;
    for (int i = 0; i < n; i ++) {
        if (a[i] >= r) {
            res += a[i] - r;
            l = r = a[i];
        } else if (b[i] <= l) {
            res += l - b[i];
            l = r = b[i];
        } else {
            l = max(l, a[i]);
            r = min(r, b[i]);
        }
    }
    return res;
}

K - soyorin的通知

问题可以简化成用 n n n 个物品填满 n − 1 n-1 n1 的背包空间,因为第一步只能为花费 p p p 并通知一个人,注意该操作也可以多次使用

而题述操作二限制条件为前提是第 i i i 个人已经被通知过,而 b i ≥ 1 b_i\ge 1 bi1,故可以贪心的每次都为下一次铺垫(即一定取也可以取下一次操作用到的人)

完全背包,稍微改改就能用

时间复杂度 O ( n 2 ) O(n^2) O(n2)

int f[N];

int solve() {
    cin >> n >> m;
    for (int i = 0; i < n; i ++) cin >> a[i] >> b[i];
    for (int i = 1; i < n; i ++) f[i] = i * m;
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n - 1; j ++) {
            int u = min(j + b[i], n - 1);
            f[u] = min(f[u], f[j] + a[i]);
        }
    return f[n - 1] + m;
}

L - anon的星星

时间复杂度 O ( 1 ) O(1) O(1)

void solve() {
    cin >> n >> m;
    int x = n + m >> 1, y = n - m >> 1;
    if (n + m & 1) cout << -1 << endl;
    else cout << x << ' ' << y << endl;
}

M - mutsumi的排列连通

若存在以下情况,答案为 1 1 1

X 1 X X -> X   X X
X 1 X X    X   X X

X X 1 X -> X X   X
X 1 X X    X   X X

排除以上情况后

n = 1 n=1 n=1 n = 2 n=2 n=2 则均无答案

n = 1
1
1

n = 2
1 2
1 2

n ≥ 5 n\ge 5 n5 时,可以构造删除第三列元素,始终符合

X X 1 X X X
X X 2 X X X

n = 3 n=3 n=3 n = 4 n=4 n=4,通过枚举法发现,答案也为 2 2 2

时间复杂度 O ( 1 ) O(1) O(1)

int solve() {
    cin >> n;
    map<int, int> A, B;
    for (int i = 1; i <= n; i ++) cin >> a[i], A[a[i]] = i;
    for (int i = 1; i <= n; i ++) cin >> b[i], B[b[i]] = i;
    a[n + 1] = b[n + 1] = 0;
    int cnt = 0;
    for (int i = 1; i <= n; i ++) {
        if (a[i] == b[i] && i != 1 && i != n) return 1;
        if (a[i] == b[i + 1] || a[i + 1] == b[i]) return 1;
        if (a[i] == b[i]) cnt ++;
    }
    if (n <= 2) return -1;
    return 2;
}
  • 14
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JorbanS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值