牛客周赛 Round 71 题解

牛客周赛 Round 71 题解

A 构造A+B

容易想出最多有 \(n - 1\) 种构造方法,所以只要判断 \(n\)\(k\) 的关系即可。

#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\) 四种字符构造,可以这样 \(\underline{a}bcda\underline{b}cdab\underline{c}dabc\underline{d}\) 其中下划线是原来的字符串中的内容。

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

#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\) 的全排列之一,所以我们也是枚举全排列,然后对于每一种排列进行枚举,具体方式如下:

设当前的排列的颜色为 \(a_0, a_1, a_2\)

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

可以将最后的气球分成三块区域,其中 \([1, i - 1]\) 是颜色 \(a_0\)\([i,j-1]\) 是颜色 \(a_1\)\([j,n]\) 是颜色 \(a_2\) 。那么当前的消耗就是

\[pre_{n,a_2} - pre_{j - 1, a_2} + pre_{j-1,a_1} - pre_{i-1, a_1} + pre_{i - 1, a_0} \]

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

我们考虑优化,可以只枚举其中一个端点,例如我们从右往左枚举 \(j\),那么我们只需要在 \(j\) 之前找到一个位置 \(i\) ,使得 \(pre_{i,a_0} - pre_{i,a_1}\) 最小即可。这个可以另外开一个数组 \(mn\)\(mn_j\) 表示前 \(j\) 个位置中最小的 \(pre_{i, a_0} - pre_{i,a_1}\),预处理好这个前缀最小值即可。

需要注意的是,可能最后气球只会被染成一种颜色或者两种颜色,所以我们在枚举 \(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(n \log n)\)

#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 变化的数组

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

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

\[a_i^{(0)} = a_i \\ a_i^{(j)} = a_i^{(j-1)} + a_i^{(j-1)} \& m \]

那么期望值为

\[\frac{\sum_{j=0}^t C_k^j \cdot a_i^{(j)} + (2^k-\sum_{j=0}^t C_k^j) \cdot a_i^{(t)}}{2^k} \]

直接写就行了,时间复杂度 \(O(n \cdot \log_2 \min(k,30) \cdot \log_2 (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 @ 2024-12-09 11:14  Time_Limit_Exceeded  阅读(37)  评论(0编辑  收藏  举报