2024牛客寒假算法基础集训营1

A-DFS搜索

暴力判就行

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

void solve() {
    int n;
    string s;
    cin >> n >> s;
    int f = 0;
    for (int i = 0; i < n; i++) {
        if (s[i] != 'D') continue;
        for (int j = i + 1; j < n; j++) {
            if (s[j] != 'F') continue;
            for (int k = j + 1; k < n; k++) {
                if (s[k] != 'S') continue;
                f = 1;
                break;
            }
            break;
        }
        break;
    }
    cout << f << " ";
    f = 0;
    for (int i = 0; i < n; i++) {
        if (s[i] != 'd') continue;
        for (int j = i + 1; j < n; j++) {
            if (s[j] != 'f') continue;
            for (int k = j + 1; k < n; k++) {
                if (s[k] != 's') continue;
                f = 1;
                break;
            }
            break;
        }
        break;
    }
    cout << f << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    for (cin >> T; T; T--)
        solve();
    return 0;
}

B-关鸡

只要分别堵住左右的边界就行。但是有一种特殊的情况就是左右的边界共用一个\((0,2)\)点的情况。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;


void solve() {

    int n;
    vector<set<int>> l(2), r(2);
    cin >> n;
    int a = 2, b = 2;
    for (int x, y; n; n--) {
        cin >> x >> y, --x;
        if (y <= 0) l[x].insert(y), a = 1;
        if (y >= 0) r[x].insert(y), b = 1;
    }
    for (int x = 0; x < 2 and a == 1; x++) {
        for (const auto &y: l[x]) {
            if (l[x ^ 1].count(y)) {
                a = 0;
                break;
            } else if (l[x ^ 1].count(y - 1)) {
                a = 0;
                break;
            } else if (l[x ^ 1].count(y + 1)) {
                a = 0;
                break;
            }
        }
    }
    for (int x = 0; x < 2 and b == 1; x++) {
        for (const auto &y: r[x]) {
            if (r[x ^ 1].count(y)) {
                b = 0;
                break;
            } else if (r[x ^ 1].count(y - 1)) {
                b = 0;
                break;
            } else if (r[x ^ 1].count(y + 1)) {
                b = 0;
                break;
            }
        }
    }
    int ans = 3;
    if (l[0].count(-1)) ans--;
    if (l[1].count(0)) ans--;
    if (r[0].count(1)) ans--;
    cout << min(a + b, ans) << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    for (cin >> T; T; T--)
        solve();
    return 0;
}

C-按闹分配

首先可以想到最优的分配方法就是用时少的人先办理。

可以推出的是,插队插在\(x\)个人前面则\(S_c-S_{min}=xt_c\le M\),所以算\(x\)在求个和即可。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, q, tc;
    cin >> n >> q >> tc;
    vi t(n);
    for (auto &i: t) cin >> i;
    t.push_back(0);
    sort(t.begin(), t.end());
    for (int i = 1; i <= n; i++)
        t[i] += t[i - 1];
    for (int m, x; q; q--) {
        cin >> m;
        x = m / tc;
        x = max(0ll, n - x);
        cout << t[x] + tc<< "\n";
    }
    return 0;
}

D-数组成鸡

因为询问的范围并不算很大,并且最终的结果要乘积。所以其实整个数组可以改变的部分并不多。可以把每一个数变成0,然后再这个数的周围暴力枚举即可,只要超出边界就可以结束。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, q;
    cin >> n >> q;
    vi a(n);
    for (auto &i: a) cin >> i;
    sort(a.begin(), a.end());
    set<int> cnt;
    auto check = [a, &cnt](int x) {
        i128 ans = 1;
        for (const auto &i: a) {
            ans = ans * (x + i);
            if (ans > 1e9 or ans < -1e9)break;
        }
        if (ans > 1e9 or ans < -1e9) return false;
        cnt.insert(ans);
        return true;
    };
    for (int i = 0; i < n; i++) {
        if (i and a[i] == a[i - 1]) continue;
        for (int j = -a[i] - 1; check(j); j--);
        for (int j = -a[i] + 1; check(j); j++);
    }
    cnt.insert(0);
    for (int x; q; q--) {
        cin >> x;
        if (cnt.count(x)) cout << "Yes\n";
        else cout << "No\n";
    }
    return 0;
}

E-本题又主要考察了贪心

因为\(m\)只有\(10\)所以\(3^{10}\)级别的暴力枚举是可以接受的

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int n, m, res;
vector<int> a, x, y;

void dfs(int i) {
    if (i == m) {
        int ans = 1;
        for (int i = 1; i < n; i++)
            ans += (a[i] > a[0]);
        res = min(res, ans);
        return;
    }
    a[x[i]] += 3;
    dfs(i + 1);
    a[x[i]] -= 3;

    a[y[i]] += 3;
    dfs(i + 1);
    a[y[i]] -= 3;

    a[x[i]]++, a[y[i]]++;
    dfs(i + 1);
    a[x[i]]--, a[y[i]]--;

    return;
}

void solve() {

    cin >> n >> m, res = inf;
    a.resize(n), x.clear(), y.clear();
    for (auto &i: a) cin >> i;
    for (int u, v; m; m--) {
        cin >> u >> v, u--, v--;
        if (u == 0 or v == 0) a[0] += 3;
        else x.push_back(u), y.push_back(v);
    }
    m = x.size();
    dfs(0);
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    for (cin >> T; T; T--)
        solve();
    return 0;
}

F-鸡数题!

根据题目,其实可以转化为第二类斯特林数。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    if (n < m) {
        cout << "0\n";
        return 0;
    }
    vi fact(n + 1);
    fact[0] = 1;
    for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % mod;
    int res = 0;
    for (int i = 0, t; i <= m; i++) {
        t = power(i, n) * inv(fact[i]) % mod * inv(fact[m - i]) % mod;
        if ((m - i) % 2 == 0) res = (res + t) % mod;
        else res = (res - t + mod) % mod;
    }
    cout << res << "\n";
    return 0;
}

why买外卖

把所有的优惠卷按照\(a_i\)排序,然后枚举在前\(i\)个卷全部使用的情况下,是否可以满足原价大于等\(a_i\)

也就是

\[m+\sum_{j=1}^{i} b_j \ge a_i \]

是否成立

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;



void solve() {
    int n, m;
    cin >> n >> m;
    vector<pii> a(n);
    for (auto &[x, y]: a) cin >> x >> y;
    sort(a.begin(), a.end());
    int res = m , cnt = m;
    for (const auto &[x, y]: a) {
        cnt += y;
        if( cnt >= x) res = max( res , cnt);
    }
    cout << res << "\n";

}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    for (cin >> T; T; T--)
        solve();
    return 0;
}

H-01背包,但是bit

如果要使得当前背包中的物品重量不超过\(x\)只需要选择所有满足\(x|w_i = x\)的物品即可。

在这种情况下,也就是要枚举所有的\(x\le m\),然后计算出结果即可。

但是显然在满足小于等于\(m\)的情况下,二进制下1的个数越多越好。所以我们可以枚举\(m\)二进制下所有为\(1\)的位,然后把这一位变位\(0\),把低位全部变为\(1\)来做\(x\)。这样符合条件的\(x\)不超过30个。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}

void solve() {
    int n, m;
    cin >> n >> m;
    vi v(n), w(n);
    int res = 0;
    for (int i = 0; i < n; i++) cin >> v[i] >> w[i];
    auto calc = [v, w, n](int x) {
        int ans = 0;
        for (int i = 0; i < n; i++)
            if ((x | w[i]) == x) ans += v[i];
        return ans;
    };
    res = max(res, calc(m));
    for (int i = 0; i < 31; i++)
        if (m & (1 << i)) res = max(res, calc(m ^ (1 << i) | ((1 << i) - 1)));
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int TC;
    for (cin >> TC; TC; TC--)
        solve();
    return 0;
}

I-It's bertrand paradox. Again!

两种方案,bit的方法相当于先随机出\(x,y\)在随机出\(r\),buaa的方法相当于每次都直接随机出\((x,y,r)\),自然的bit的分布更加均匀,而buaa的方法则相对集中在中心部位。

所以写出这两种方法,分别随机100次,然后看圆心在中间的情况(\(-50\le x ,y\le 50\))的概率,bit的概率大约是\(0.25\),buaa的概率大约是\(0.5\)

根据这个分辨一下就好。

test()是我的暴力代码

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}

void test() {
    mt19937 mt{random_device()()};
    uniform_int_distribution rdx(-99, 99);
    uniform_int_distribution rdr(1, 100);
    int n = 1e5;
    auto buaa = [&]() {
        vector<array<int, 3>> a;
        for (int t = n, x, y, r; t; t--) {
            while (true) {
                x = rdx(mt), y = rdx(mt), r = rdr(mt);
                if (r < 0 or abs(x + r) > 100 or abs(x - r) > 100 or abs(y + r) > 100 or abs(y - r) > 100) continue;
                break;
            }
            a.push_back({x, y, r});
        }
        return a;
    };
    auto bit = [&]() {
        vector<array<int, 3>> a;
        for (int t = n, x, y, r; t; t--) {
            x = rdx(mt), y = rdx(mt);
            while (true) {
                r = rdr(mt);
                if (r < 0 or abs(x + r) > 100 or abs(x - r) > 100 or abs(y + r) > 100 or abs(y - r) > 100) continue;
                break;
            }
            a.push_back({x, y, r});
        }
        return a;
    };

    int t = 100;
    ldb x = 0, y = 0;
    for (int i = 1; i <= t; i++) {
        ldb xx = 0, yy = 0;
        for (auto [x, y, r]: bit())
            if (abs(x) <= 50 and abs(y) <= 50) xx += 1;
        for (auto [x, y, r]: buaa())
            if (abs(x) <= 50 and abs(y) <= 50) yy += 1;
        x += xx / n, y += yy / n;
        cerr << "bit = " << xx / n << " buaa = " << yy / n << " \n";
    }
    x /= t, y /= t;
    cout << fixed << setprecision(6);
    cout << "bit = " << x << " buaa = " << y << " \n";

}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
//    test();
    int n;
    cin >> n;
    ldb cnt = 0;
    for( int i = 1 , x , y , r ; i <= n ; i ++ ){
        cin >> x >> y >> r;
        if( abs(x) <= 50 and abs(y) <= 50 ) cnt += 1;
    }
    cnt /= n;
    if( abs( cnt - 0.25) < abs(cnt-0.5)) cout << "bit-noob\n";
    else cout << "buaa-noob\n";
    return 0;
}

J-又鸟之亦心

\(i\)时刻,一定有一个人位于\(a_i\) ,用set维护另一个人可能位于的位置,保证整个过程种set必须保证非空。这样我们就可以二分答案。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, x, y;
    cin >> n >> x >> y;
    vi a(n);
    for (auto &i: a) cin >> i;
    int l = 0, r = 1e9, res = -1;
    auto check = [=](int d) -> bool {
        int lst = y;
        set<int> s;
        if (abs(x - y) <= d) s.insert(x);
        else return false;
        for (int i: a) {
            if ( abs(i - lst) <= d) s.insert(lst);
            while (not s.empty() and *s.begin() < i - d) s.erase(*s.begin());
            while (not s.empty() and *s.rbegin() > i + d) s.erase(*s.rbegin());
            lst = i;
            if (s.empty()) break;
        }
        return not s.empty();
    };

    for (int mid; l <= r;) {
        mid = (l + r) / 2;
        if (check(mid)) res = mid, r = mid - 1;
        else l = mid + 1;
    }
    cout << res << "\n";
    return 0;
}

K-牛镇公务员考试

\(i\)可以影响\(a_i\),所以图一定是一个内向基环树森林。

基环树上的链无法影响答案,因为可以从环上的结点反推出答案。对于任意一个环,随便选择一个点,然后枚举所有的答案,最后判断一下即可。

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int i64

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 998244353;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vi a(n);
    vector<string> s(n);
    vi vis(n), inDeg(n);
    for (int i = 0; i < n; i++) cin >> a[i] >> s[i], a[i]--, inDeg[a[i]]++;
    queue<int> q;
    for (int i = 0; i < n; i++)
        if (inDeg[i] == 0) q.push(i);
    for (int x, y; not q.empty();) {
        x = q.front(), q.pop();
        vis[x] = 1, y = a[x];
        if (--inDeg[y] == 0) q.push(y);
    }
    int res = 1;
    for (int i = 0, ans; i < n; i++) {
        if (vis[i]) continue;
        ans= 0, vis[i] = 1;
        for (int u = 0, v, t; u < 5; u++) {
            t = i, v = u;
            do {
                v = s[t][v] - 'A';
                t = a[t];
                vis[t] = 1;
            } while (t != i);
            ans += v == u;
        }
        res = res * ans % mod;
    }
    cout << res << "\n";
    return 0;
}

L-要有光

根据高中的地理知识就可以猜出,当太阳的高度为0时,影子面积最大

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
using ldb = long double;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

int power(int x, int y) {
    int ans = 1;
    while (y) {
        if (y & 1) ans = ans * x % mod;
        x = x * x % mod, y /= 2;
    }
    return ans;
}

int inv(int x) {
    return power(x, mod - 2);
}


void solve() {
    ldb d, c, h, w, x, y, res;
    cin >> c >> d >> h >> w;
    x = 2 * c, y = 2 * w;
    res = x * y - c * w;
    cout << fixed << setprecision(8) << res << "\n";
    return;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    for (cin >> T; T; T--)
        solve();
    return 0;
}

M-牛客老粉才知道的秘密

#include<bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;

#define int long long

using vi = vector<int>;

using pii = pair<int, int>;
using vii = vector<pii>;

const int inf = INT_MAX, INF = LLONG_MAX;
const int mod = 1e9 + 7;
const vi dx = {0, 0, 1, -1}, dy = {1, -1, 0, 0};
using edge = array<int, 3>;

void solve() {
    int n, res = 0;
    cin >> n;
    res = n / 6;
    if (n % 6 != 0) res = res * 2;
    cout << res << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    for (cin >> T; T; T--)
        solve();
    return 0;
}
posted @ 2024-02-04 18:36  PHarr  阅读(34)  评论(0编辑  收藏  举报