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

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

A

解题思路:

按照dfs出现顺序暴力判断即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    vector<bool> vis(40);
    for (auto c : s)
    {
        if (c == 'D')
        {
            vis['D' - 'A'] = true;
        }
        else if (c == 'F' && vis['D' - 'A'] == true)
        {
            vis[c - 'A'] = true;
        }
        else if (c == 'S' && vis['F' - 'A'] == true)
        {
            vis[c - 'A'] = true;
        }
    }
    if (vis['D' - 'A'] && vis['F' - 'A'] && vis['S' - 'A'])
    {
        cout << 1 << ' ';
    }
    else
    {
        cout << 0 << ' ';
    }
    for (int i = 0; i < 30; i++)
    {
        vis[i] = false;
    }
    for (auto c : s)
    {
        if (c == 'd')
        {
            vis['d' - 'a'] = true;
        }
        else if (c == 'f' && vis['d' - 'a'] == true)
        {
            vis[c - 'a'] = true;
        }
        else if (c == 's' && vis['f' - 'a'] == true)
        {
            vis[c - 'a'] = true;
        }
    }
    if (vis['d' - 'a'] && vis['f' - 'a'] && vis['s' - 'a'])
    {
        cout << 1 << ' ';
    }
    else
    {
        cout << 0 << ' ';
    }
    cout << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

B

解题思路:

如果(2,0),(1,1),(1,1)这三处有火,那么三把火就够了。

其余封锁起码四把火,左右各两把。

暴力记录枚举情况添火判断即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n;
    cin >> n;
    set<pii> s;
    bool l = false;
    bool tr = false;
    ll res = 3;
    ll minx = 1e18;
    ll maxx = -1e18;
    for (int i = 1; i <= n; i++)
    {
        ll r, c;
        cin >> r >> c;
        minx = min(minx, c);
        maxx = max(maxx, c);
        if (r == 1)
        {
            if (s.find({r + 1, c}) != s.end())
            {
                if (c < 0)
                {
                    l = true;
                }
                else if (c > 0)
                {
                    tr = true;
                }
            }
            if (s.find({r + 1, c - 1}) != s.end())
            {
                if (c - 1 >= 0)
                {
                    tr = true;
                }
                else
                {
                    l = true;
                }
            }
            if (s.find({r + 1, c + 1}) != s.end())
            {
                if (c + 1 > 0)
                {
                    tr = true;
                }
                else
                {
                    l = true;
                }
            }
        }
        else
        {
            if (s.find({r - 1, c}) != s.end())
            {
                if (c < 0)
                {
                    l = true;
                }
                else if (c > 0)
                {
                    tr = true;
                }
            }
            if (s.find({r - 1, c - 1}) != s.end())
            {
                if (c - 1 >= 0)
                {
                    tr = true;
                }
                else
                {
                    l = true;
                }
            }
            if (s.find({r - 1, c + 1}) != s.end())
            {
                if (c + 1 > 0)
                {
                    tr = true;
                }
                else
                {
                    l = true;
                }
            }
        }
        s.insert({r, c});
    }
    if (s.find({2, 0}) != s.end())
    {
        res--;
    }
    if (s.find({1, -1}) != s.end())
    {
        res--;
    }
    if (s.find({1, 1}) != s.end())
    {
        res--;
    }
    ll ans = 0;
    if (!l)
    {
        if (s.size())
        {
            auto x = minx;
            if (x <= 0)
            {
                ans += 1;
            }
            else
            {
                ans += 2;
            }
        }
        else
        {
            ans += 2;
        }
    }
    // cout << ans << endl;
    if (!tr)
    {
        if (s.size())
        {
            auto x = maxx;
            if (x >= 0)
            {
                ans += 1;
            }
            else
            {
                ans += 2;
            }
        }
        else
        {
            ans += 2;
        }
    }

    ans = min(ans, res);
    cout << ans << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

C

解题思路:

先求出总不满意度S

ScSminM

SminScM

尝试插入发现,越往后插队,Smin越大,具有单调性,二分即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    ll n, q, t;
    cin >> n >> q >> t;
    vector<ll> a(n + 1), pre(n + 10);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    sort(a.begin() + 1, a.end());
    ll sum = 0;
    for (int i = 1; i <= n; i++)
    {
        sum += (n - i + 1) * a[i];
        pre[i] = a[i] + pre[i - 1];
    }
    while (q--)
    {
        ll m;
        cin >> m;
        auto check = [&](int mid)
        {
            ll cur = t * (n - mid + 1);
            return cur <= m;
        };
        int l = 0;
        int r = n + 1;
        while (l + 1 < r)
        {
            int mid = l + r >> 1;
            if (check(mid))
            {
                r = mid;
            }
            else
            {
                l = mid;
            }
        }
        cout << pre[r - 1] + t << endl;
    }
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

D

解题思路:

(109M109)

累乘发现,9!×9!>109。也就是说出现19个不同的数字后怎么改绝对值都会大于109。当然,0除外。

实际上,我们将这少数不同的数分别变成从0开始不断累加或者累减,哪怕n=2,最多109次之后,序列累乘值一定突破询问上限。

所以,我们可以O(n109)内求出所有可得到的数字。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int N = 1e5 + 10;
const int inf = 1e9;
int n, q;
ll a[N];
set<ll> s, ans;

bool check(ll x)
{
    ll res = 1;
    for (int i = 1; i <= n; i++)
    {
        res *= a[i] + x;
        if (abs(res) > inf)
        {
            return false;
        }
    }
    ans.insert(res);
    return true;
}

void solve()
{
    cin >> n >> q;
    ans.insert(0);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        s.insert(a[i]);
    }
    if (s.size() < 20)
    {
        // 将i从1/-1开始递增和递减,最多根号n次
        for (auto i : s)
        {
            ll cur = -i + 1;
            while (check(cur))
            {
                cur++;
            }
            cur = -i - 1;
            while (check(cur))
            {
                cur--;
            }
        }
    }
    while (q--)
    {
        int x;
        scanf("%d", &x);
        if (ans.count(x))
        {
            puts("Yes");
        }
        else
        {
            puts("No");
        }
    }
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

E

解题思路:

数据范围很小,对于每场比赛对决枚举所有胜负可能。

O(m×3m)

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(n + 1);
    vector<pii> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        if (u == 1 || v == 1)
        {
            a[1] += 3;
            continue;
        }
        q.push_back({u, v});
    }
    m = q.size();
    ll ans = 1e18;
    ll sum = pow(3, m);
    for (int i = 0; i < sum; i++)
    {
        ll t = i;
        vector<int> p;
        vector<int> b = a;
        for (int i = 1; i <= m; i++)
        {
            p.push_back(t % 3);
            t /= 3;
        }
        int cur = 0;
        for (auto x : p)
        {
            int u = q[cur].fi;
            int v = q[cur].se;
            cur++;
            if (x == 0)
            {
                b[u]++;
                b[v]++;
            }
            else if (x == 1)
            {
                b[u] += 3;
            }
            else
            {
                b[v] += 3;
            }
        }
        ll cnt = 0;
        for (int i = 1; i <= n; i++)
        {
            if (b[i] > b[1])
            {
                cnt++;
            }
        }
        ans = min(ans, cnt + 1);
    }
    cout << ans << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

F

解题思路:

本题考察第二类斯特林数的通项公式。

S(n,m)={nm}=1m!×k=0m((1)k×(mk)×(mk)n)

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int N = 1e5;
const int mod = 1e9 + 7;
ll f[N + 1];
ll invf[N + 1];

ll qmi(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
        {
            res = res * a % mod;
        }
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

ll C(ll a, ll b)
{
    if (b == 0 || a == b)
    {
        return 1;
    }
    return (f[a] * invf[b] % mod) * invf[a - b] % mod;
}

void solve()
{
    int n, m;
    cin >> n >> m;
    if (n < m)
    {
        cout << 0 << endl;
        return;
    }
    f[0] = 1;
    for (int i = 1; i <= N; i++)
    {
        f[i] = f[i - 1] * i % mod;
    }
    invf[N] = qmi(f[N], mod - 2) % mod;
    for (int i = N - 1; i; i--)
    {
        invf[i] = invf[i + 1] * (i + 1) % mod;
    }
    ll ans = 0;
    for (int i = 0; i <= m; i++)
    {
        ll sign = (i & 1) ? -1 : 1;
        ans = (ans + (sign * C(m, i) * qmi(m - i, n) % mod)) % mod;
        ans = (ans + mod) % mod;
    }
    ans = ans * invf[m] % mod;
    cout << ans << endl;
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

G

解题思路:

显然,满减券的时候小的都叠加起来,手机里的钱加满减能得到上限。记得将手机里没用完的钱加上。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    ll n, m;
    cin >> n >> m;
    vector<pii> v(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i].fi >> v[i].se;
    }
    sort(v.begin() + 1, v.end());
    ll pre = 0;
    ll ans = m;
    for (int i = 1; i <= n; i++)
    {
        pre += v[i].se;
        if (v[i].fi - pre <= m)
        {
            ans = max(ans, v[i].fi + m - (v[i].fi - pre));
        }
    }
    cout << ans << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

H

解题思路:

我们总重量枚举二进制中所有的1位,将该为改为0,同时低位全部变为1进行讨论,记得最后总重量本身讨论一下。

举例:m=1101010100

将其划分为几段

[0,0111111111]

[1000000000,1011111111]

[1100000000,1100111111]

[1101000000,1101001111]

[1101010000,1101010011]

[1101010100]

m中有5个一,所以前五段对应的就是这些一的变化讨论。最后一个就是总重量本身讨论。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n, m;
    cin >> n >> m;
    vector<int> v(n + 1), w(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> v[i] >> w[i];
    }
    ll ans = 0;
    ll cur = 0;
    ll res = 0;
    for (int i = 30; i >= 0; i--)
    {
        if (m >> i & 1)
        {
            res = 0;
            ll t = cur + (1 << i) - 1;
            for (int j = 1; j <= n; j++)
            {
                if ((w[j] | t) == t)
                {
                    res += v[j];
                }
            }
            ans = max(ans, res);
            cur += 1 << i;
        }
    }
    res = 0;
    for (int i = 1; i <= n; i++)
    {
        if ((w[i] | cur) == cur)
        {
            res += v[i];
        }
    }
    ans = max(ans, res);
    cout << ans << endl;
}

int main()
{

    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

I

解题思路:

按照题目所给构造方法进行构造,然后统计二者某一数值的平均或者其他统计特征,找到明显区别,即可判断。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    ll n;
    cin >> n;
    n = 1e5;
    double s = 0;
    for (int i = 1; i <= n; i++)
    {
        ll a, b, c;
        cin >> a >> b >> c;
        s += c;
    }
    s /= n;
    if (s >= 20)
    {
        puts("buaa-noob");
    }
    else
    {
        puts("bit-noob");
    }
}

int main()
{

    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

J

解题思路:

二分。

每次二分判断尝试构造任务过程,使得二者过程期间最大距离小于mid,存在则说明mid在逼近。

在构造过程当中,假设我们当前遍历到了i处,此时有一个人一定在ai处。查看另一位可出现的位置集合。((x,y))

如果存在abs(aiaj)>mid,此时我们就应当将aj移出集合,因为aj参与构建这一步会使得过程最大值过大。如果将集合内所有aj删除后,集合为空,则说明这一步二者间的距离不可能小于等于mid

对于i1删去的aj,即使abs(ai+1aj)midaj理论上已不可能和ai+1同时出现促成一对。

因为,如果ai+1的对象能选aj,说明这一步一定是有一个人从ai走到ai+1,因为在i时刻一定有一个人在ai

我们按照时间倒推,如果i+1时刻两人位置是(ai+1,aj),那么i时刻两人位置就是(ai,aj),即非法状态。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n, x, y;
    cin >> n >> x >> y;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    ll l = -1;
    ll r = 1e9 + 1;
    auto check = [&](ll mid)
    {
        set<int> s;
        if (abs(x - y) > mid)
        {
            return false;
        }
        s.insert(x);
        s.insert(y);
        for (int i = 1; i <= n; i++)
        {
            while (s.size() && abs((*s.begin()) - a[i]) > mid)
            {
                s.erase(s.begin());
            }
            while (s.size() && abs((*s.rbegin()) - a[i]) > mid)
            {
                s.erase(*s.rbegin());
            }
            if (s.empty())
            {
                return false;
            }
            s.insert(a[i]);
        }
        return true;
    };
    while (l + 1 < r)
    {
        ll mid = l + r >> 1;
        if (check(mid))
        {
            r = mid;
        }
        else
        {
            l = mid;
        }
    }
    cout << r << endl;
}

int main()
{
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

K

解题思路:

n个点,n条边。基环树。

找到每个环。对该环搜索。每个结点开始有5种选择,依次顺着选下去,如果能沿着环走回来,说明得到一份使得该环上所有题都正确的答案集合。

将所有环的正确答案集合数量累乘,即答案。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;
const int mod = 998244353;
void solve()
{
    int n;
    cin >> n;
    vector<int> a(n + 1);
    vector<string> s(n + 1);
    vector<int> indeg(n + 1);
    vector<bool> vis(n + 1);
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i] >> s[i];
        indeg[a[i]]++;
    }
    queue<int> q;
    for (int i = 1; i <= n; i++)
    {
        if (!indeg[i])
        {
            q.push(i);
        }
    }
    while (q.size())
    {
        auto t = q.front();
        q.pop();
        indeg[a[t]]--;
        vis[t] = true;
        if (!indeg[a[t]])
        {
            q.push(a[t]);
        }
    }
    ll ans = 1;
    for (int i = 1; i <= n; i++)
    {
        if (vis[i])
        {
            continue;
        }
        int u = i;
        int v = u;
        while (true)
        {
            vis[v] = true;
            v = a[v];
            if (v == u)
            {
                break;
            }
        }
        int cnt = 0;
        for (int j = 0; j < 5; j++)
        {
            v = u;
            int idx = j;
            while (true)
            {
                idx = s[v][idx] - 'A';
                v = a[v];
                if (v == u)
                {
                    break;
                }
            }
            if (s[u][idx] == s[u][j])
            {
                cnt++;
            }
        }
        ans = ans * cnt % mod;
    }
    cout << ans << endl;
}

int main()
{

    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

L

解题思路:

简单画个图,答案为3wc

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    ll c,d,h,w;
    cin >> c >> d >> h >> w;
    cout << 3 * w * c << endl;
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}

M

解题思路:

分别从左和右开始排放。

如果总长度是6的倍数只需从任意一端排放依次,否则就是两次排放可能之和。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<ll, ll>;
#define fi first
#define se second
using i128 = __int128_t;
using piii = pair<ll, pair<ll, ll>>;

void solve()
{
    int n;
    cin >> n;
    if (n % 6 == 0)
    {
        cout << n / 6 << endl;
    }
    else
    {
        cout << (n / 6) * 2 << endl;
    }
}

int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }

    return 0;
}
posted @   value0  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)
点击右上角即可分享
微信分享提示