2022牛客寒假训练营(五)全部题解

A:疫苗小孩

题意 | 简单

  • 打疫苗一共可以打三针,且只有后两针接种才能提升手速,且一天最多只能打一针。后一针疫苗与前一针疫苗之间相隔k天对手速的提升最为明显,每偏离一天则效果会想应减少,每偏离一天则效果减少q的话,则后一针疫苗实际对人手速的影响为w - |k - p| * q
  • n天以后就是比赛,只考虑n天内疫苗的接种,但这n天中只能挑没有牛客比赛的日子去打疫苗
    n n n ≤ \leq 1 0 6 10^6 106 , 1 ≤ \leq k , w , q k,w,q k,w,q ≤ \leq 1 0 9 10^9 109

思路 | 贪心

  • 枚举第二针所在的位置pos ,二分找到离pos+k和pos-k最近的两个点计算最大贡献就可以啦

代码

#include 
#define int long long
#define pb push_back 
using namespace std;
const int maxn = 1e6 + 100;
int a[maxn];
void solve() {
    int n; string s; cin >> n >> s;
    s = '.' + s;
    int k, w, q;cin >> k >> w >> q;
    int cnt = 0;
    a[cnt] = n + 1;
    for (int i = 1; i <= n; i++)  if (s[i] == '0')a[++cnt] = i;
    a[cnt + 1] = n + 1;
    int maxx = 0;
    for (int i = 1; i <= cnt; i++) {
        int posb = a[i];
        int posa1 = a[lower_bound(a + 1, a + 1 + cnt, posb - k) - a - 1];
        int posa2 = a[lower_bound(a + 1, a + 1 + cnt, posb - k) - a];
        int posc1 = a[lower_bound(a + 1, a + 1 + cnt, posb + k) - a - 1];
        int posc2 = a[lower_bound(a + 1, a + 1 + cnt, posb + k) - a];
        if (posa1 < posb && posb <= a[cnt]) maxx = max(maxx, w - abs(posb + k - posa1) * q);
        if (posa2 < posb && posb <= a[cnt]) maxx = max(maxx, w - abs(posb + k - posa2) * q);
        if (posa1 < posb && posb < posc1 && posc1 <= a[cnt]) maxx = max(maxx, 2 * w - abs(posa1 + k - posb) * q - abs(posb + k - posc1) * q);
        if (posa1 < posb && posb < posc2 && posc2 <= a[cnt]) maxx = max(maxx, 2 * w - abs(posa1 + k - posb) * q - abs(posb + k - posc2) * q);
        if (posa2 < posb && posb < posc1 && posc1 <= a[cnt]) maxx = max(maxx, 2 * w - abs(posa2 + k - posb) * q - abs(posb + k - posc1) * q);
        if (posa2 < posb && posb < posc2 && posc2 <= a[cnt]) maxx = max(maxx, 2 * w - abs(posa2 + k - posb) * q - abs(posb + k - posc2) * q);
    }
    cout << maxx << endl;
}
signed main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int t; t = 1;
    while (t--) {
        solve();
    }
    return 0;
}


C:战棋小孩

题意 | 简单

  • 进行 n n n局游戏,每局游戏可以从两个英雄种选择一位,同时如果选择礼遇的话就会拥有再多两个英雄的选择。现在给出每局游戏的选将情况和结束后上榜需要的分数,和每局游戏结束后,排行榜上需要的分数,现在想直到通过能力合理排列并进行最优选择后,他最多因上榜而开心的次数。
    n , k , s n,k,s nks分别表示游戏局数,可使用礼遇的次数和初试分数
    0 ≤ \leq n , k n,k nk ≤ \leq 20 20 20

思路 | 枚举

  • 二进制枚举一下哪些位置需要使用礼遇,然后再排序一下(从大到小)就可以了(证明),按照累加是判断否可以得分

代码

#include 
#define int long long
#define pb push_back
using namespace std;
const int maxn = 1e6 + 100;
const int mod = 1e9 + 7;
struct node {
    int val, maxval;
}a[maxn];
int p[maxn];
int b[maxn];
bool cmp(int a, int b) {
    return a > b;
}
void solve() {
    int n, k, s;
    cin >> n >> k >> s;
    int maxx = 0;
    for (int i = 0; i < n; i++) cin >> p[i];
    for (int i = 0; i < n; i++) {
        int aa, bb, c, d;
        cin >> aa >> bb >> c >> d;
        a[i].val = max(aa, bb);
        a[i].maxval = max(a[i].val, max(c, d));
    }
    for (int i = 0; i < (1 << n); i++) {
        int cnt = 0;
        for (int j = 0; j < n; j++) {
            if (i & (1 << j)) b[j] = a[j].maxval, cnt++;
            else b[j] = a[j].val;
        }
        if (cnt <= k) {
            sort(b, b + n, cmp);
            int sum = s;
            int ans = 0;
            for (int j = 0; j < n; j++) {
                sum += b[j];
                if (sum >= p[j]) ans++;
            }
            maxx = max(maxx, ans);
        }
    }
    cout << maxx << endl;
}
signed main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int t; t = 1;
    while (t--) {
        solve();
    }
    return 0;
}

D:数位小孩

题意 | 简单

  • 给定一个区间 [ l , r l,r l,r] ,求这个区间内有多少个数字满足以下条件:

    1. 每相邻两个数位和为素数
    2. 其中至少一个数位为1
    3. 没有前导零
  • 0 ≤ \leq l , r l,r lr ≤ \leq 1 0 10 10^{10} 1010

思路 | 暴力

  • 因为数据范围不大,可以直接写一个dfs进行暴力寻找,把哪些数字可以相邻在一起的先预处理出来,然后就暴力开找就行啦

代码

#include 
#define int long long
#define pb push_back 
using namespace std;
const int maxn = 1e6 + 100;
typedef long long ll;
ll prime[1006];
bool sf[2006]; 
void sushu() {   
    ll num = 0; 
    memset(sf, true, sizeof(sf));
    sf[1] = false;
    sf[0] = false; 
    for (int i = 2; i < 1000; i++) {          
        if (sf[i]) prime[++num] = i;      
        for (int j = 1; j <= num; j++) {       
            if (i * prime[j] > 1000) break; 
            sf[i * prime[j]] = false;      
            if (i % prime[j] == 0) break;   
        }
    }
}
int l, r;
int ans1 = 0; int ans2 = 0;
vector<int>g[100];
void dfs(int x,int sum,int f) {
    if (sum < l) {
        if (f == 1) ans1++;
    }
    else return;
    for (int i = 0; i < g[x].size(); i++) {
        int to = g[x][i];
        if (to == 1)  dfs(to, sum * 10 + to, 1);
        else dfs(to, sum * 10 + to, f);
    }
}
void dfs2(int x, int sum,int f) {
    if (sum <= r) {
        if (f == 1) ans2++;
    }
    else return;
    for (int i = 0; i < g[x].size(); i++) {
        int to = g[x][i];
        if (to == 1)  dfs2(to, sum * 10 + to, 1);
        else dfs2(to, sum * 10 + to, f);
    }
}
void solve() {     
    for (int i = 0; i <= 9; i++) {
        for (int j = 0; j <= 9; j++) {
            if (sf[i + j])g[i].push_back(j);
        }
    }
    cin >> l >> r;
    for (int i = 1; i <= 9; i++) {
        if (i == 1) {
            dfs(i, i, 1);
            dfs2(i, i, 1);
        }
        else {
            dfs(i, i, 0);
            dfs2(i, i, 0);
        }
    }
    cout << ans2 - ans1 << endl;
}
signed main() {
    int t; t = 1;
    sushu();
    while (t--) {
        solve();
    }
    return 0;
}


E:复苏小孩

题意 | 简单

  • 给一串长度为 n n n的字符串,字符串里面的字符仅包含 ‘1’,‘2’,‘3’,分别表示使用鬼眼,鬼影,鬼手的能力。每使用其中一个鬼的能力,这个鬼就会吸收其他两个鬼各一半的能力,且假设这三种鬼的能力值为1,按照这个字符串顺序使用对应鬼的能力后,将三种鬼的力量值%998244353代入

  • m m m次操作,每行三个整数
    1 x y : 表示修改字符串的第x位修改成数字y
    2 x y : 表示查询 l,r 这段字符串

  • 0 ≤ \leq n , m n,m nm ≤ \leq 1 0 5 10^{5} 105

思路 | 线段树

你可能感兴趣的:(dp,动态规划,思维,c++,动态规划,数据结构)