牛客周赛 Round 71 题解

牛客周赛 Round 71 题解

A 构造A+B

容易想出最多有 n1 种构造方法,所以只要判断 nk 的关系即可。

#include <bits/stdc++.h>
using namespace std;
int main() {
	int n, k; cin >> n >> k;
    if (k <= n - 1) {
        cout << "YES\n";
    } else {
        cout << "NO\n";
    }
		return 0;
}

B 宝石手串

遇到这种问题,我们先思考什么情况下输出 1 :当手串内没有重复出现的字符时。所以这个需要加特判。

然后思考,为了去掉最少的宝石,我们需要找到(在环上)离得最近的两个宝石,然后去掉它们中间的宝石即可。如果需要考虑环的话,那么我们在串的遍历上需要从尾回到头,这样是很麻烦的。事实上对于这种环上的问题,我们可以使用「破环成链」的方法。

具体来看,就是将原字符串 S 变成 S+S ,这样就可以让头尾相连,然后就将原来的环上问题转化为了串上问题。

对于拼接后的字符串,我们遍历一遍找到最近的两个相同字符的位置即可,记得判断 1 的情况。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;

void solve() {
    int n; cin >> n;
    string s; cin >> s;
    if (n == 2) {
        if (s[0] != s[1]) cout << -1 << endl;
        else cout << 0 << endl;
        return ;
    }
    for (int i = 0; i < n - 1; i ++ ) {
        if (s[i] == s[i + 1]) {
            cout << 0 << endl;
            return ;
        }
    }
    if (s[0] == s[n - 1]) cout << 0 << endl;
    else {
        s = s + s;
        vector<int> pos(30, -1);
        int ans = INF;
        for (int i = 0; i < 2 * n; i ++ ) {
            int c = s[i] - 'a';
            if (pos[c] != -1 && pos[c] != i - n) {
                ans = min(ans, i - pos[c] - 1);
            }
            pos[c] = i;
        }
        if (ans == INF) ans = -1;
        cout << ans << endl;
    }
}

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

C 最小循环节

脑筋急转弯题。首先我们思考一个问题:假如说原字符串中只有 abcd 四种字符,那么在添加的时候有没有必要加入没出现过的字符?

其实是没必要的,例如对于 abcd 来说,我们可以只用 abcd 四种字符构造,可以这样 a_bcdab_cdabc_dabcd_ 其中下划线是原来的字符串中的内容。

所以最后的答案就是原字符串中字符的种类。

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;

int p[2222];

void solve() {
    string s; cin >> s;
    int n = s.size();
    int cnt = 0;
    for (int i = 0; i < n; i ++ ) {
        if (p[s[i]]) continue;
        p[s[i]] ++;
        cnt ++;
    }
    cout << cnt << endl;
}

int main() {
	int T = 1;
    while (T -- )
        solve();
	return 0;
}

D 气球谜题

官方题解是DP做法,我们这里使用前缀和来做。

由于最后的颜色排列是 3 的全排列之一,所以我们也是枚举全排列,然后对于每一种排列进行枚举,具体方式如下:

设当前的排列的颜色为 a0,a1,a2

定义前缀和数组 prei,j 表示将前 i 个气球全部变成 j 颜色所需的时间,这个递推就可以解决,具体看代码。

可以将最后的气球分成三块区域,其中 [1,i1] 是颜色 a0[i,j1] 是颜色 a1[j,n] 是颜色 a2 。那么当前的消耗就是

pren,a2prej1,a2+prej1,a1prei1,a1+prei1,a0

看出我们需要枚举两个端点 i,j 来实现这个,但是这样的话就是 O(n2) 的算法。

我们考虑优化,可以只枚举其中一个端点,例如我们从右往左枚举 j,那么我们只需要在 j 之前找到一个位置 i ,使得 prei,a0prei,a1 最小即可。这个可以另外开一个数组 mnmnj 表示前 j 个位置中最小的 prei,a0prei,a1,预处理好这个前缀最小值即可。

需要注意的是,可能最后气球只会被染成一种颜色或者两种颜色,所以我们在枚举 j 的时候需要考虑好范围,不要漏掉特况。

时间复杂度 O(n) 带点常数。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;

int t[N];
LL pre[N][3];
LL mn[N];
int a[3] = {0, 1, 2};

void solve() {
 	int n;
    cin >> n;
    string s; cin >> s;
    for (int i = 1; i <= n; i ++ ) cin >> t[i];
    for (int i = 1; i <= n; i ++ ) {
        pre[i][0] = pre[i - 1][0];
        pre[i][1] = pre[i - 1][1];
        pre[i][2] = pre[i - 1][2];
        if (s[i - 1] == '0') {
            pre[i][1] += t[i];
            pre[i][2] += t[i];
        } else if (s[i - 1] == '1') {
            pre[i][0] += t[i];
            pre[i][2] += t[i];
        } else {
            pre[i][0] += t[i];
            pre[i][1] += t[i];
        }
    }
  	LL ans = INF;
    do {
        mn[1] = pre[1][a[0]] - pre[1][a[1]];
        for (int i = 2; i <= n; i ++ ) {
            mn[i] = min(mn[i - 1], pre[i][a[0]] - pre[i][a[1]]);
        }
        for (int i = n + 1; i >= 1; i -- ) {
            ans = min(ans, pre[n][a[2]] - pre[i - 1][a[2]] + pre[i - 1][a[1]] + mn[i - 1]);
        }
    } while (next_permutation(a, a + 3));
    cout << ans << endl;
}

int main() {
	int T = 1;
    while (T -- )
        solve();
	return 0;
}

E 三角谜题

容易想到,我们可以枚举底边,然后直接找最长的数量大于等于2的边作为腰即可。

由于边长很长,所以可以使用 map 来当桶。

时间复杂度 O(nlogn)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;

map<int, int> f;

void solve() {
    cout << fixed << setprecision(8);
    f.clear();
    int n; cin >> n;
    for (int i = 1; i <= n; i ++ ) {
        int x, y;
        cin >> x >> y;
        f[x] += y;
    }
    vector<int> v1;
    double ans = -1;
    bool flag = false;
    for (auto kv : f) {
        if (kv.second >= 2) {
            v1.push_back(kv.first);
        }
    }
    if (v1.empty()) {
        cout << -1 << endl;
        return ;
    }
    for (auto kv : f) {
        int k = kv.first;
        int v = kv.second;
        int j = v1.size() - 1;
        while (j >= 0 && v1[j] == k && f[v1[j]] == 2) j --;
        if (j < 0) continue;
        int len = v1[j];
        if (len + len <= k) continue;
        double s = (double)k * 0.5 * sqrt(1ll * len * len - 1ll * k * k * 0.25);
        flag = true;
        ans = max(ans, s);
    }
    if (flag) cout << ans << endl;
    else cout << -1 << endl;
}

int main() {
	int T = 1;
    cin >> T;
    while (T -- )
        solve();
	return 0;
}

F 变化的数组

感觉是一道很诈骗的题目。事实上我们打表可以发现,在经过一定操作次数后 ai&m 的值就变成了 0 。之后的按位与操作都是无效的。具体的证明我认为 这篇博客 写的就不错,大家可以去看看。

对于某个数 ai ,一共经过了 k 次操作,其中 t 次操作过后 ai&m 就已经是 0 了,记经过了 j 次操作之后的值为 ai(j) ,所以有

ai(0)=aiai(j)=ai(j1)+ai(j1)&m

那么期望值为

j=0tCkjai(j)+(2kj=0tCkj)ai(t)2k

直接写就行了,时间复杂度 O(nlog2min(k,30)log2(1e9+7)) 当然可以通过预处理把最后一个 log 优化掉。

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const double eps = 1e-11;

LL q_pow(LL a, int b) {
    LL ans = 1;
    for (; b; b >>= 1) {
        if (b & 1) ans = ans * a % MOD;
        a = a * a % MOD;
    }
    return ans;
}

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    LL sum = 0;
    int inv2 = q_pow(2, MOD - 2);
    LL inv2k = q_pow(inv2, k);
    for (int i = 1; i <= n; i ++ ) {
        LL x; cin >> x;
        LL tmp = x;
        LL now1 = 1;
        LL now2 = (q_pow(2, k) - 1 + MOD) % MOD;
        for (int i = 1; i <= k; i ++ ) {
            LL d = x & m;
            x += d;
            if (d == 0) break;
            now1 = now1 * (k - i + 1) % MOD * q_pow(i, MOD - 2) % MOD;
            now2 = (now2 - now1 + MOD) % MOD;
            tmp = (tmp + now1 * (x % MOD) % MOD) % MOD;
        }
        tmp = (tmp + now2 * (x % MOD) % MOD) % MOD;
        tmp = tmp * inv2k % MOD;
        sum = (sum + tmp) % MOD;
    }
    cout << sum << endl;
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("1.in", "r", stdin);
	freopen("1.out", "w", stdout);
#endif
    int T = 1;
    while (T -- )
        solve();
    return 0;
}
posted @   Time_Limit_Exceeded  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示