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

A. DFS搜索

从前往后遍历字符串,根据字母出现顺序进行判断,时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int n;
    cin >> n;
    string s;
    cin >> s;
    int f = 0, F = 0;
    for(int i = 0;i < s.size();i++)
    {
        if(f == 0 && s[i] == 'd')
        f++;
        if(f == 1 && s[i] == 'f')
        f++;
        if(f == 2 && s[i] == 's')
        f++;
        if(F == 0 && s[i] == 'D')
        F++;
        if(F == 1 && s[i] == 'F')
        F++;
        if(F == 2 && s[i] == 'S')
        F++;
    }
    if(F == 3)
    cout << "1 ";
    else
    cout << "0 ";
    if(f == 3)
    cout << "1" << '\n';
    else
    cout << "0" << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

B. 关鸡

记录下所有位置,遍历,注意 ( 1 , 1 ) , ( 1 , − 1 ) , ( 2 , 0 ) (1,1),(1,-1),(2,0) (1,1),(1,1),(2,0)三个位置,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e5 + 10;
const int INF = 1e18;
const ll mod = 998244353;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    set<pii> s;
    int l = 2, r = 2;
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++)
    {
        int x, y;
        cin >> x >> y;
        s.insert({x, y});
        if(y <= 0) l = 1;
        if(y >= 0) r = 1;
    }
    for(int i = -1;i <= 1;i++)
    {
        for(auto p : s)
        {
            int x = p.first,y = p.second;
            if(s.count({x ^ 3, y + i})) 
            {
                if(y < 0) l = 0;
                if(y > 0) r = 0;
            }
        }
    }
    int ans = 3;
    if(s.count({2, 0})) ans--;
    if(s.count({1, 1})) ans--;
    if(s.count({1, -1})) ans--;
     
    cout << min(l + r, ans) << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

C. 按闹分配

不难得出,当办事时间从小到大排时总的不满意度 S S S最小

令插队后排在鸡后面的人数 x x x S c − S m i n S_c - S_{min} ScSmin即为 x ∗ t c x * t_c xtc,所以我们只需要二分找到鸡后面能排的最大人数,再加上预处理出的前若干个人的办事时间的前缀和即可,时间复杂度 O ( q l o g n ) O(qlogn) O(qlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int t[N];
void solve()
{
    int n, q, tc;
    cin >> n >> q >> tc;
    for(int i = 1;i <= n;i++)
    cin >> t[i];
    sort(t+1,t+n+1);
    for(int i = 1;i <= n;i++)
    t[i] += t[i - 1];
    while(q--)
    {
        int now;
        cin >> now;
        int l = 0, r = n;
        while(l <= r)
        {
            int mid = (l + r) / 2;
            if(mid * tc <= now)
            l = mid + 1;
            else
            r = mid - 1;
        }
        cout << t[n - l + 1] + tc << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

D. 数组成鸡

首先我们要知道,一个绝对值小于等于 1 e 9 1e9 1e9的数最多由不超过二十个不同的数相乘得来

所以我们只需要处理题目中给出不同数个数不超过二十个的情况

由于数值很小,所以我们可以遍历这些数,每次的操作次数为当前这个数 + ( − 4 e 4 , 4 e 4 ) +(-4e4,4e4) +(4e4,4e4)

注意,询问值是 0 0 0时必定可行,时间复杂度 O ( 400 1 0 9 + q l o g n ) O(400\sqrt{10^9} + qlogn) O(400109 +qlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int n, q;
    cin >> n >> q;
    map<int, int> cnt;
    for(int i = 1;i <= n;i++)
    {
        int tt;
        cin >> tt;
        cnt[tt]++;
    }
    vector<pii> a;
    for(auto x : cnt)
    a.push_back(x);
    set<int> s;
    s.insert(0);
    if(a.size() <= 20)
    {
        int M = 4e4;
        for(int k = 0;k < a.size();k++)
        {
            for(int i = -M;i <= M;i++)
            {
                int now = 1;
                int cur = i - a[k].first;
                for(auto j : a)
                {
                    int v = j.first + cur;
                    if(v == 0)
                    continue;
                    for(int t = 1;t <= j.second;t++)
                    {
                        now = now * v;
                        if(abs(now) > 1e9)
                        break;
                    }
                    if(abs(now) > 1e9)
                    break;
                }
                if(abs(now) <= 1e9)
                s.insert(now);
            }
        }
    }
    while(q--)
    {
        int x;
        cin >> x;
        if(s.count(x))
        cout << "Yes" << '\n';
        else
        cout << "No" << '\n';
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

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

由于数据较小,直接 d f s dfs dfs枚举每个情况即可,时间复杂度 O ( 3 m T ) O(3^{m}T) O(3mT)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int a[100], b[100], c[100], ans;
int n, m;
void dfs(int now)
{
    if(now == m + 1)
    {
        int cnt = 1;
        for(int i = 2;i <= n;i++)
        {
            if(a[i] > a[1])
            cnt++;
        }
        ans = min(ans, cnt);
        return;
    }
    a[b[now]] += 3;
    dfs(now + 1);
    a[b[now]] -= 3;
    a[c[now]] += 3;
    dfs(now + 1);
    a[c[now]] -= 3;
    a[b[now]]++;
    a[c[now]]++;
    dfs(now + 1);
    a[b[now]]--;
    a[c[now]]--;
}
void solve()
{
    ans = 1000;
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    cin >> a[i];
    for(int i = 1;i <= m;i++)
    cin >> b[i] >> c[i];
    dfs(1);
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

F. 鸡数题

第二类斯特林数S(n,m)的定义为: n n n个有区别球放到 m m m个无区别盒子

这题题意可以理解为有 n n n 1 1 1,把这些 1 1 1分成 m m m份,问有多少种分法,那如何与第二类斯特林数相联系呢

我们来看第二个条件,对于任意的整数 1 ≤ i ≤ m − 1 , a i < a i + 1 1≤i≤m−1,a_i<a_{i+1} 1im1ai<ai+1,这说明将 n n n个有区别球放到 m m m个无区别盒子后对每一种方案进行排序,所得到的即为题目这种情况,所以我们直接套第二类斯特林数的公式,时间复杂度 O ( m ) O(m) O(m)

S ( n , m ) = 1 m ! ∑ k = 0 m ( − 1 ) k C ( m , k ) ( m − k ) n S(n,m) = \frac{1}{m!}\sum^{m}_{k=0}(-1)^{k}C(m,k)(m-k)^{n} S(n,m)=m!1k=0m(1)kC(m,k)(mk)n

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int fac[N], infac[N];
ll C(ll n,ll m){//n个数选m个数
  return fac[n] * infac[n - m] % mod * infac[m] % mod;
}
inline ll binpow(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % mod;
        a = (ll)a * a % mod;
        b >>= 1;
    }
    return res;
}
int cnt;
int n, m;
void solve()
{
    cin >> n >> m;
    int now = 0, fh = 1;
    for(int k = 0;k <= m;k++)
    {
        now = (now + fh * C(m, k) % mod * binpow(m - k, n) % mod) % mod;
        fh = fh * (-1);
    }
    for(int i = 1;i <= m;i++)
    now = now * binpow(i, mod - 2) % mod;
    cout << (now + mod) % mod << '\n';
}
signed main()
{
    fac[0] = 1;
    infac[0] = 1;
    for (int i = 1; i <= 1e6;i++){
        fac[i] = i * fac[i - 1] % mod;
        infac[i] = binpow(fac[i], mod - 2);
    }
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

G.why买外卖

按照优惠券的要求额度从小到大排序,再进行遍历

令初始价格为手上有的钱,判断当前手上的钱是否满足要求额度,如果满足则加上当前优惠券的减去额度,如果不满足则将当前优惠券的减去额度加到下一张优惠券上,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
struct sj
{
    int a, b;
} x[N];
bool cmp(sj x, sj y)
{
    return x.a < y.a;
}
void solve()
{
    int n, m;
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    cin >> x[i].a >> x[i].b;
    sort(x+1,x+n+1,cmp);
    for(int i = 1;i <= n;i++)
    {
        if(m + x[i].b >= x[i].a)
        m += x[i].b;
        else
        x[i + 1].b += x[i].b;
    }
    cout << m << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

H. 01背包,但是bit

背包最大载重为对于 W m W_m Wm二进制数的每位 1 1 1,当我们令这位 1 1 1 0 0 0后把剩下的二进制数分成前面和后面两个部分

对于前面那一部分二进制,必须要求与 W m W_m Wm的每位都相同。对于后面那一部分二进制,没有强制要求,因为这位 1 1 1已经被我们要求为 0 0 0,所以无论如何后面怎么取,最后得到的新二进制数 W W W肯定是小于 W m W_m Wm

有一个需要注意的是,我们要计算总重量为 W m W_m Wm时的最大价值

每次拿入背包,判断当前重量 w i w_i wi的二进制数 1 1 1位是不是 W W W的二进制数 1 1 1位的子集即可,如果是子集则可以加入背包,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int v[N], w[N];
int n, W, ans;
void check(int x)
{
    int cnt = 0;
    for(int i = 1;i <= n;i++)
    {
        if((x & w[i]) == w[i])
        cnt += v[i];
    }
    ans = max(ans, cnt);
}
void solve()
{
    ans = 0;
    cin >> n >> W;
    for(int i = 1;i <= n;i++)
    cin >> v[i] >> w[i];
    check(W);
    for(int i = 30;i >= 0;i--)
    {
        if((1 << i) & W)
        check((W ^ (1 << i)) | ((1 << i) - 1));
    }
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

I. It’s bertrand paradox. Again!

一个人是不满足的话重新生成 x x x y y y,另一个人是不满足的话重新生成 r r r

我们直接来看生成圆的区间, x x x y y y都在 [ − 100 , 100 ] [-100,100] [100,100],我们取这个平面的四个角落,边长都为 10 10 10,那么一次生成随机数落在这四个角落里的概率是多少呢, 10 ∗ 10 ∗ 4 200 ∗ 200 = 1 100 \frac{10*10*4}{200*200}=\frac{1}{100} 20020010104=1001

因为生成圆的数量固定为 1 0 5 10^5 105,所以理论上第二个人生成的圆中,有 1000 1000 1000个落在角落里,而第一个人生成的圆落在角落里的数目要远远小于这个值,时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int n;
    cin >> n;
    int a, b, c, cnt = 0;
    for(int i = 1;i <= n;i++)
    {
        cin >> a >> b >> c;
        if((abs(100 - a) <= 10 || abs(-100 - a) <= 10) && (abs(100 - b) <= 10 || abs(-100 - b) <= 10))
        cnt++;
    }
    if(cnt > 400)
    cout << "bit-noob" << '\n';
    else
    cout << "buaa-noob" << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

K. 牛镇公务员考试

题面简直是非常难理解,简单的说就是一题的答案决定下一题的答案,问所有题都答对有多少种情况,答案对 998244353998244353 998244353998244353 998244353998244353取模

一道题决定另一道题,总共有 n n n道题和 n n n个决定关系,而且决定关系是单向的,那我们就能想到内向基环树。

我们把题目抽象为图形,一个有向图,里面有若干的环和环上的链

令所求 a n s = 1 ans=1 ans=1

对于每个环上的链,由于决定关系都是前一个决定后一个,所以答案都是唯一的,对 a n s ans ans没影响

对于每个环,我们可以随便选一个点作为起点,然后让起点选项为 A ∼ E A\sim E AE,再沿着环进行遍历,如果最后推出的起点选项确实为开始时候选的那个选项,那么这个环的可行方案 r e s = r e s + 1 res = res + 1 res=res+1,由于选项个数为 5 5 5,所以每个环的可行方案为 [ 0 , 5 ] [0,5] [0,5],每次解决完一个环后, a n s = a n s ∗ r e s % m o d ans = ans * res \% mod ans=ansres%mod即可,时间复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
#define all(a) a.begin(), a.end()
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 1e5 + 10;
const int INF = 1e18;
const ll mod = 998244353;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int n;
    cin >> n;
    vector<int> a(n+5), vis(n+5, 0);
    vector<string> s(n+5);
    for(int i = 1;i <= n;i++)
    cin >> a[i] >> s[i];
    int ans = 1;
    for(int i = 1;i <= n;i++)
    {
        int j = i;
        vector<int> v;
        while(!vis[j])
        {
            vis[j] = 1;
            v.push_back(j);
            j = a[j];
        }
        auto pos = find(all(v), j);
        if(pos != v.end())
        {
            int res = 0;
            v.erase(v.begin(), pos);  // 去掉环上的链
            for(int k = 0;k < 5;k++)
            {
                int now = k;
                for(auto u : v)
                now = (s[u][now] - 'A');
                if(k == now)
                res++;
            }
            ans = ans * res % mod;
        }
    }
    cout << ans << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    //cin >> t;
    while (t--)
    solve();
    return 0;
}

L. 要有光

一开始读错题目了,以为要求计算墙上黑暗的最大面积…

地上的最大未被着照亮面积为梯形,直接计算即可,时间复杂度 O ( T ) O(T) O(T)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int c, d, h, w;
    cin >> c >> d >> h >> w;
    cout << w * c * 3 << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}

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

好吧我不是老粉所以不知道秘密(

直接在纸上画一下 n n n是不是 6 6 6的倍数的两种情况,输出答案即可,时间复杂度 O ( T ) O(T) O(T)

#include <bits/stdc++.h>
#define int long long
#define YES "YES"
#define NO "NO"
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-9;
const int N = 5e6 + 10;
const int INF = 1e18;
const ll mod = 1e9 + 7;
const int base = 13331;
mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
void solve()
{
    int n;
    cin >> n;
    if(n % 6 == 0)
    cout << n / 6 << '\n';
    else
    cout << n / 6 * 2 << '\n';
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--)
    solve();
    return 0;
}
  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值