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

A:DFS搜索

字符串s 中判断是否存在子序列DFS以及子序列dfs。遍历先找D 再找F 再找S即可 dfs同理

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
void solve(){
    int n; cin >> n;
    string s; cin >> s;
    int flag = 0, ans1 = 0, ans2 = 0;
    for(int i = 0;i < n;i++){
        if(s[i] == 'D' && flag == 0)
            flag = 1;
        else if(s[i] == 'F' && flag == 1){
            flag = 2;
        }
        else if(s[i] == 'S' && flag == 2){
            flag = 3;
        }
    }
    if(flag == 3) ans1 = 1;
    flag = 0;
     for(int i = 0;i < n;i++){
        if(s[i] == 'd' && flag == 0)
            flag = 1;
        else if(s[i] == 'f' && flag == 1){
            flag = 2;
        }
        else if(s[i] == 's' && flag == 2){
            flag = 3;
        }
    }
    if(flag == 3) ans2 = 1;
    cout << ans1 << ' ' << ans2 << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

B:关鸡

要关住鸡其实就是将两边都关住。那么便存在两种方法,一个是(2, 0) (1, -1) (1, 1)的三角形关住鸡。另外的便是边上管道形成的障碍,比如(x, 1) (x, 2) 或 (x, 1) (x + 1, 2)。
那么我们用flag表示(2, 0)是否有火;flag3 和 flag4 分别表示 (1, -1) 和 (1, 1)是否有火;flag1和flag2用于表示左端管道和右端管道的状态,0 代表一个火都没有, 1 代表有火但是没有封住路,2 代表已经封住路了。

那么接下来只需要分类讨论即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
pii q[N];
void solve(){
    int n; cin >> n;
    int flag = 0, flag3 = 0, flag4 = 0;
    for(int i = 1;i <= n;i++){
        int r, c; cin >> r >> c;
        q[i] = {c, r};
        if(c == 0 && r == 2)
            flag = 1;
        if(c == -1 && r == 1)
            flag3 = 1;
        if(c == 1 && r == 1)
            flag4 = 1;
    }
    sort(q + 1, q + 1 + n);
    int flag1 = 0, flag2 = 0;
    for(int i = 1;i <= n;i++){
        if(q[i].first < 0)
            flag1 =1 ;
        else if(q[i].first > 0)
            flag2 = 1;
    }
    for(int i = 2;i <= n;i++){
        if(q[i].first <= 0){
            if(q[i].first == q[i - 1].first)
                flag1 = 2;
            else if(q[i].first == q[i - 1].first + 1 && q[i].second != q[i - 1].second)
                flag1 = 2;
            
        }
        else if(q[i].first > 0){
            if(q[i].first == q[i - 1].first)
                flag2 = 2;
            else if(q[i].first == q[i - 1].first + 1 && q[i].second != q[i - 1].second)
                flag2 = 2;
        }
    }
    int ans = 0;
    if(flag1 == 0){
        if(flag2 == 0){
            if(flag == 1)
                ans = 2;
            else
                ans = 3;
        }
        else if(flag2 == 1){
            if(flag == 1 || flag4 == 1)
                ans = 2;
            else
                ans = 3;
        }
        else if(flag2 == 2){
            if(flag == 1)
                ans = 1;
            else
                ans = 2;
        }
    }
    else if(flag1 == 1){
        if(flag2 == 0){
            if(flag3 == 1 || flag == 1)
                ans = 2;
            else
                ans = 3;
        }
        else if(flag2 == 1){
            if(flag3 == 1 && flag4 == 1)
                ans = 1;
            else
                ans = 2;
        }
        else if(flag2 == 2)
            ans = 1;
    }
    else{
        if(flag2 == 0){
            if(flag == 1)
                ans = 1;
            else
                ans = 2;
        }
        else if(flag2 == 1){
            ans = 1;
        }
        else if(flag2 == 2)
            ans = 0;
    }
    //cout << flag << ' ' << flag1 << ' ' << flag2  << ' '<< flag3  << ' ' << flag4 << endl;
    cout << ans << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

C:按闹分配

要总不满意度最小,那么显然是从小到大安排最好。而假设鸡插队后面有K个人,那么其实就是这K个人多等了 \(t_c\)时间,总忍耐度就多了\(K * t_c\) 时间,那么对于容忍限度M找出最大的K 然后插入计算前面需要等的时间即可。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int a[N], sum[N];
void solve(){
    int n, m, t;
    cin >> n >> m >> t;
    for(int i = 1;i <= n;i++)
        cin >> a[i];
    sort(a + 1,a + 1 + n);
    for(int i = 1;i <= n;i++)
        sum[i] =  sum[i - 1] + a[i];
    while(m--){
        ll ren; cin >> ren;
        ll cnt = min(n, ren / t);
        ll ans = sum[n - cnt] + t;
        cout << ans << endl;
    }
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    //cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

D:数组成鸡

M的大小为\([-1e9,1e9]\) ,那么对于不同的数字相乘,其实最多接受不到20个不同的数字。那用map记录不同的数字以及出现次数,若是数字种类大于20直接排除;接下来考虑20种及以内的数字种类,

由于n 大于等于2,那么对于所有乘积结果,一定有一个数在\([-\sqrt{1e9}, \sqrt{1e9}]\)之间,所以对于每种数,去遍历区间\([-1e5,1e5]\) 即可得到所有的能得到的乘积。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int a[N];
map<int,int>mp;
void solve(){
    int n, q; cin >> n >> q;
    for(int i = 1;i <= n;i++)
        cin >> a[i];
    sort(a + 1,a + 1 + n);
    for(int i = 1;i <= n;i++)
        mp[a[i]]++;
    set<int> st;
	st.insert(0);
    if(mp.size() <= 20){
        for(auto it : mp){
            for(int i = -1e5;i <= 1e5;i++){
                int cur = it.first - i;
                int ans = 1;
                for(auto j : mp){
                    int u = j.first - cur;
                    if(u == 1) continue;
                    if(u == -1){
                        if(j.second % 2)
                            ans *= -1;
                        continue;
                    }
                    for(int k = 0;k < j.second;k++){
                        ans = ans * u;
                        if(abs(ans) > 1e9)
                            break;
                    }
                    if(abs(ans) > 1e9)
                        break;
                }
                if(abs(ans) < 1e9)
                    st.insert(ans);
            }
        }
    }
    while(q--){
        int m; cin >> m;
        if(st.count(m))
            cout << "Yes\n";
        else
            cout << "No\n";
    }
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    //cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

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

观察n m都极小,考虑暴力,直接爆搜所有情况即可,对于每一场比赛有三种结局。复杂度为\(O(m ^ 3 * T)\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
pii q[15];
int a[15];
int n, m, ans = INF;
void dfs(int now){
    if(now > m){
        int cnt = 1;
        for(int i = 2;i <= n;i++)
            if(a[i] > a[1]) cnt++;
        ans = min(ans, cnt);
        return;
    }
    int u = q[now].first, v = q[now].second;
    a[u] += 3;
    dfs(now + 1);
    a[u] -= 3;

    a[u] += 1; a[v] += 1;
    dfs(now + 1);
    a[u] -= 1; a[v] -= 1;

    a[v] += 3;
    dfs(now + 1);
    a[v] -= 3;
}
void solve(){
    
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
        cin >> a[i];
    for(int i = 1;i <= m;i++){
        int u, v; cin >> u >> v;
        q[i] = {u, v};
    }
    ans = INF;
    dfs(1);
    cout << ans << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

F:鸡数题!

\(a_1∣a_2∣...∣a_{m−1}∣a_m=2^n−1\) 这个就说明m个数能组成n个二进制的全部的位

对于任意的 \(i≠j\) ,满足$a_i $ & $ a_j=0$ 这个代表没有m个数之间没有重复的二进制位

所以就相当于 n 个二进制数分到 m 个数里

对于任意的整数\(1≤i≤m−1\)\(a_i<a_{i+1}\) 所以说明 这n个数之间是不一样的

那就相当于 n 个不同的球 放到 m 个不同的箱子里 有几种方法,那就是第二类斯特林数(具体内容可查询博客)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int fac[N], infac[N];
ll C(ll n,ll m){//n个数选m个数
  return fac[n] * infac[n - m] % mod * infac[m] % mod;
}
long long binpow(long long a, long long b, long long m) {//a为底数 b为指数 m为模数
  a %= m;
  long long res = 1;
  while (b > 0) {
    if (b & 1) res = res * a % m;
    a = a * a % m;
    b >>= 1;
  }
  return res;
}

void solve(){
    int n, m; cin >> n >> m;
    ll ans = 1;
    for(int i = 1;i <= m;i++)
        ans = (ans * i) % mod;
    ans = binpow(ans, mod - 2, mod);
    ll sum = 0;
    for(int k = 0;k <= m;k++){
        if(k % 2 == 0)
            sum = (sum + (C(m, k)  % mod * binpow(m - k, n, mod) % mod)) % mod;
        else
            sum = (sum - (C(m, k)  % mod * binpow(m - k, n, mod) % mod)) % mod;
    }
    ans = (ans * sum) % mod;
    cout << (ans + mod )  % mod << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    //cin >> TTT;
    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, mod);
    }
    while (TTT--)
        solve();
    return 0;
}

G:why买外卖

则所有满足\(x >= a_i\)的满减优惠都可以一起同时被使用,那么我们可以先对满减券按\(a_i\)从小到大排序,那么对于所有\(i >= 2\) \(a_i\)可以使用的时候,前面的也都可以使用,所以此时就贪心遍历找最大能接受的额度即可。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
pii q[N];
int n, m;

void solve(){
    cin >> n >> m;
    ll ans = m;
    for(int i = 1;i <= n;i++){
        int u, v; cin >> u >> v;
        q[i] = {u, v};
    }
    sort(q + 1,q + 1 + n);
    ll sum = 0;
    for(int i = 1;i <= n;i++){
        sum += q[i].second;
        if(q[i].first - sum <= m)
            ans = max(ans, m + sum);
    }
    cout << ans << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

H:01背包,但是bit

设我们最后选择的重量和为 c。对于m 枚举每一位是1的bit。假设当前枚举到下标为 i ,只需要将该位设为0 ,那么对于\([30, i + 1]\) 的位置,c 必须是 m 的子集这样才能保证c 不大于m;对于\([i - 1, 0]\) 就可以任意选择,为了贪心所以全部都选1。再加上等于c == m的情况,那么 c 的所有情况便有了。对于所有情况再去比较谁获得的价值和最大即可。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int v[N], w[N], n;
ll check(ll m){
    ll res = 0;
    for(int i = 1;i <= n;i++){
        if((w[i] & m) == w[i])  
            res += v[i];
    }
    return res;
}
void solve(){
    int m; cin >> n >> m;
    for(int i = 1;i <= n;i++)
        cin >> v[i] >> w[i];
    ll ans = check(m);
    for(int i = 29;i >= 0;i--)
        if((1 << i) & m)
            ans = max(ans, check((m ^ (1 << i)) | ((1 << i) - 1)));
    cout << ans << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

I:It's bertrand paradox. Again!

这题比较玄学,就是看概率。第一种选中边缘的位置是与其他位置概率相同;但是第二种要则几乎是第一种的百分之一,因为要同时要求半径很小。 所以接近平均概率的就是第一种,否则就是第二种。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;

void solve(){
    int n; cin >> n;
    int cnt = 0;
    for(int i = 1;i <= n;i++){
        int x, y, r;
        cin >> x >> y >> r;
        if(abs(x) >= 98 && abs(y) >= 98)
            cnt++;
    }
    if(cnt >= 10)
        cout << "bit-noob\n";
    else
        cout << "buaa-noob\n";
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    //cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

K:牛镇公务员考试

题面很绕,但是理解之后就会发现,所有点都只有一个出度。然后只需要找到所有的环,在这环上任意找一个点作为起点,暴力5种情况并且走一圈看看是否存在矛盾,若是无矛盾则这环内的方案++,最后答案便是所有环的方案相乘。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
struct node{
	int to;
	string s;
}a[N];
int ind[N], tod[N];
vector<int>G[N];
int check(int indx){
	int ans = 0;
	for(int i = 0;i < 5;i++){
		int pos = indx, choice = i;
		while(1){
			choice = a[pos].s[choice] - 'A';
			pos = a[pos].to;
			if(pos == indx){
				if(choice == i) ans++;
				break;
			}
		}
	}
	return ans;
}
void solve(){
    int n; cin >> n;
	for(int i = 1;i <= n;i++){
		cin >> a[i].to >> a[i].s;
		G[i].push_back(a[i].to);
		tod[i]++; ind[a[i].to]++;
	}
	ll ans = 1;
	vector<int> vis(n + 10, -1);
	for(int i = 1;i <= n;i++){
		int u = i;
		while(vis[u] == -1){
			vis[u] = i;
			u = a[u].to;
		}
		if(vis[u] == i){
			int res = check(u);
			ans = (ans * res) % 998244353;
		}
	}


	cout << ans << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    //cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

L:要有光

诈骗题,仔细阅读题面发现要求的是地面的阴影而不是墙上的。那就是梯形的面积

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
int a[N], sum[N];
void solve(){
    int c, d, h, w; cin >> c >> d >> h >> w;
    cout << 3 * w * c << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

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

首先看 n 是否为 6 的倍数。

  • n是6的倍数,那么从左开始和从右开始其实会重合,所以答案就是n / 6;

  • n不是6的倍数,那么从左开始和从右开始就是错开的,答案便是n / 6 * 2;

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
typedef long long ll;
typedef pair<int, int> pii;
const double eps = 1e-10;
const int N = 3e6 + 10;
const int INF = 1e8;
const ll mod = 1e9 + 7;
void solve(){
    int n; cin >> n;
    int ans = 0;
    if(n % 6 == 0){
        ans = n / 6;
    }
    else
        ans = n / 6 * 2;
    cout  << ans << endl;
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0); std::cout.tie(0);
    int TTT = 1;
    cin >> TTT;
    while (TTT--)
        solve();
    return 0;
}

posted @ 2024-02-05 11:25  ckop  阅读(1)  评论(0)    收藏  举报