2024牛客寒假算法基础训练营2

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

注:本题解只涉及赛时思路和赛后补题收获,人菜佬勿喷

第一场六题,第二场还是六题呜呜呜,感觉比第一场要难点,线段树题没调出来,狠狠哭住了

赛时六题【ABEFIJ】补题【KDG】

A.Tokitsukaze and Bracelet

A-Tokitsukaze and Bracelet_2024牛客寒假算法基础集训营2 (nowcoder.com)

手环有3种属性:普通攻击百分比加成,体力,精神。每次炼成手环时,会对手环的每个属性都随机赋予强化等级,每个属性的强化等级可能为 +0,+1,+2 。强化等级对应的属性值如下:

  • 对于普通攻击百分比加成来说: +0 为 100% , +1 为 150% , +2 为 200% ;
  • 对于体力和精神来说: +0 会在 {29,30,31,32} 里随机选择, +1 会在 {34,36,38,40} 里随机选择, +2 固定为 45 。

例如,一个普通攻击百分比加成 100% ,体力 45 ,精神 40 的手环的强化等级为 +3 。其中普通攻击力百分比提供了 +0 ,体力提供了 +2 ,精神提供了 +1 。

分析:

签到题,根据题意直接写即可

代码:

void solve(){
    int n;
    cin >> n;
    int a, b, c;
    for(int i = 0; i < n; i++){
        int ans = 0;
        cin >> a >> b >> c;
        if(a == 100);
        else if(a == 150)
            ans++;
        else
            ans += 2;
        if(b >= 29 && b <= 32);
        else if(b >= 34 && b <= 40)
            ans++;
        else
            ans += 2;
        if(c >= 29 && c <= 32);
        else if(c >= 34 && c <= 40)
            ans++;
        else
            ans += 2;
        cout << ans << endl;
    }
}

B.Tokitsukaze and Cats

B-Tokitsukaze and Cats_2024牛客寒假算法基础集训营2 (nowcoder.com)

 

分析:

这个题目也不难,要想把一只猫封起来需要把四周都封起来,猫还有可能跳出边界,所以即使在边界也要设置防护网。只需把猫的坐标的四周的边界用map存一下,每只猫需要的防护网直接用4减去已经存在的边界的数量即可

代码:

void solve(){
    int n, m, k;
    cin >> n >> m >> k;
    map<pair<int, int>, int>mp;
    int x, y;
    int ans = 0;
    while(k--){
        cin >> x >> y;
        ans += 4;
        ans -= mp[{x - 1, y}];
        ans -= mp[{x + 1, y}];
        ans -= mp[{x, y - 1}];
        ans -= mp[{x, y + 1}];
        mp[{x, y}]++;
    }
    cout << ans;
}

E.Tokitsukaze and Eliminate (easy)

E-Tokitsukaze and Eliminate (easy)_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析:

这个题直接贪心即可,先记录每个颜色出现的数量,然后从后往前遍历直到出现的颜色等于总数,然后消除即可

这个思路同样适用于hard版本的F题。

代码:

void solve(){
    int n;
    cin >> n;
    vector<ll>a(n + 1);
    map<ll, ll>m;
    map<ll, ll>mp;
    rep(i, 1, n + 1){
        cin >> a[i];
        m[a[i]]++;
    }
    ll pos = 0, ans = 0, cnt = n, p = n;
    for(int i = n; i >= 1; i--){
        if(mp[a[i]] == 0){
            mp[a[i]]++;
            pos += m[a[i]];
        }
        if(pos == cnt){
            pos = 0;
            mp.clear();
            ans++;
            for(int j = i; j <= p; j++)
                m[a[j]]--;
            p = i - 1;
            cnt = i - 1;
        }
    }
     cout << ans << endl;
}

F.Tokitsukaze and Eliminate (hard)

F-Tokitsukaze and Eliminate (hard)_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析:

同e题

代码:

同e题

I.Tokitsukaze and Short Path (plus)

I-Tokitsukaze and Short Path (plus)_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析:

赛时读题:完全图,最短路,好!跑dij!已经把Dijkstra板子写上去后仔细一看数据范围和题目,发现这就是找规律的题,不需要跑dij。

从数据范围看,直接暴力加权值是会超时的,所以需要寻找更优解。

plus版本是取两个点之间权值较大的那个权值的两倍,可以发现权值越小的,出现次数越少,摸一下样例可以发现从小到大的权值分别出现了0,2,4,6,8...(n - 1)* 2次。所以只需将所有权值从小到大排序,for循环遍历一遍,答案加上对应的权值乘上对应的出现次数即可

代码:

void solve(){
    ll n;
    cin >> n;
    vector<ll>w(n + 1);
    for(int i = 1; i <= n; i++){
        cin >> w[i];
    }
    sort(w.begin() + 1, w.end());
    ll ans = 0;
    for(int i = 1; i <= n; i++){
        ans += (i - 1) * 2 * w[i];
    }
    cout << ans * 2 << endl;
}

J.Tokitsukaze and Short Path (minus)

J-Tokitsukaze and Short Path (minus)_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析:

minus版本与plus版本的区别就是这个题的任意两个点之间的边权成了最小值的两倍。

如果与i题一样每次都取最小值的话是无法通过的,因为会有情况的最短路不是直接连接。

不难发现,最短路无非就只有两种情况:1、两个点直接到达。2、通过最小权值的点进行转接(可以证明没有任何转接方式的权值小于与最小权值转接)

所以只需要统计两点直接距离小于最小权值的四倍的权值即可。(四倍是因为到达最小权值的点需要两倍的最小权值,再从最小权值点到目标点又需要两倍的最小权值)

代码:

void solve(){
    ll n;
    cin >> n;
    vector<ll>w(n), cnt;
    for(int i = 0; i < n; i++){
        cin >> w[i];
    }
    sort(w.begin(), w.end());
    for(int i = 1; i < n; i++){
        if(w[i] < 2 * w[0])
            cnt.push_back(w[i]);
    }
    ll ans = 0;
    ans += w[0] * (n - 1) * 2 * 2;//最小权值的点所连接的点
    ll sum = 0, flag = 0, h = 0;
    for(int i = 0; i < cnt.size(); i++){
        ans += 2 * cnt[i] * (n - 2 - i) * 2;//直接连接小于四倍最小权值的情况
        sum += (n - 2 - i) * 2;
    }
    if((n - 2) * (n - 1) - sum > 0)
        ans += ((n - 2) * (n - 1) - sum) * w[0] * 4;//需要通过最小权值的转接的情况
    cout << ans << endl;
}

K.Tokitsukaze and Password (easy)

K-Tokitsukaze and Password (easy)_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析:

数据范围很小可以直接暴搜,dfs或者循环枚举都可以

代码:

string x, y;
int n, m;
int ans;
map<char, int>mp;
map<string, int>t;
void dfs(string s, int m, map<char, int> mp) {
    if(m == n){
        int a = stoi(s);
        int b = stoi(y);
        if(s[0] >= '1' and a <= b and (a % 8 == 0)){
            ans++;
        } 
        else if(n == 1){
            if (a <= b and (a % 8 == 0)) {
                ans++;
            }
        }
    } 
    else{
        if(s[m] == '_'){
            string ss = s;
            for(char a = '0'; a <= '9'; a++){
                ss[m] = a;
                dfs(ss, m + 1, mp);
            }
        } 
        else if(s[m] <= 'z' and s[m] >= 'a'){
            for (char a = '0'; a <= '9'; a++){
                string ss = s;
                if (mp[a] == true)
                    continue;
                else {
                    for (int i = 0; i < n; i++){
                        if (ss[i] == s[m]) ss[i] = a;
                    }
                    mp[a] = true;
                    dfs(ss, m + 1, mp);
                    mp[a] = false;
                }
            }
        } 
        else
            dfs(s, m + 1, mp);
    }
}
void solve(){
    ans = 0;
    cin >> n;
    cin >> x >> y;
    dfs(x, 0, mp);
    ans %= mod;
    cout << ans << endl;
}

D.Tokitsukaze and Slash Draw

D-Tokitsukaze and Slash Draw_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析

这个题赛时的想法是dp,可无奈是个dp废物,不会写dp

赛后发现这问题看成一张图的话,其实就是求k%n之后到0的最短路

所以我们只需要建图跑一下Dijkstra

这个题是求k%n到0的最短路,所以把Dij板子稍微一改即可

代码:

基于链式前向星的Dijkstra

const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 5e3 + 10;
ll n, m, s, k;
ll u[N], v[N];
ll cnt, head[N];
ll dist[N], vis[N];
struct Edge{
    ll to, dis, next;
}edge[5000050];
void Add_edge(int from, int to, int w){
    edge[cnt].to = to;
    edge[cnt].dis = w;
    edge[cnt].next = head[from];
    head[from] = cnt++;
}
void init() {//初始化链式前向星           
    for (ll i = 0; i < N; ++i) {
        edge[i].next = -1;
        head[i] = -1;
        dist[i] = INF;
        vis[i] = 0;
    }
    cnt = 0;
}
void Dijkstra(ll s){
    priority_queue<pair<ll, ll>, vector<pair<ll, ll>>, greater<pair<ll, ll>>> q;
    q.push({0, s});
    dist[s] = 0;
    while(!q.empty()){
        ll now = q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now] = 1;
        for(ll i = head[now]; ~i; i = edge[i].next)
        {
            ll j = edge[i].to;
            if(dist[now] + edge[i].dis < dist[j]){
                dist[j] = dist[now] + edge[i].dis;
                q.push({dist[j], j});
            }
        }
    }
}
void solve(){
    cin >> n >> m >> k;
    init();
    k %= n;
    for(ll i = 1; i <= m; i++)
        cin >> u[i] >> v[i];
    for(ll i = 0; i < n; i++)
        for(ll j = 1; j <= m; j++)
            Add_edge(i, (i + u[j]) % n, v[j]);
    Dijkstra(k);
    if(dist[0] == INF)
        cout << -1 << endl;
    else
        cout << dist[0] << endl;
}

G.Tokitsukaze and Power Battle (easy)

G-Tokitsukaze and Power Battle (easy)_2024牛客寒假算法基础集训营2 (nowcoder.com)

分析:

读题可以发现单点修改,区间查询,很浓的线段树味道

a_i的范围可以看出: a_i必定是大于0的整数,也就意味着在i到j的区间中选取的两段必定是 和[i,j−1]和[j] 两段才满足题意两端的差值最大。所以我们只需用线段树维护区间和,区间最大值即可

代码:

const ll INF = 1e16 + 10;
const int N = 5e5 + 10;
ll n, m;
ll a[N];
struct segtree{
    ll sum;
    ll ans;
    ll le;
}tr[N << 2];
 
void push_up(segtree &u, segtree &l, segtree &r){
    u.sum = l.sum + r.sum;
    u.le = max(l.le, l.sum + r.le);
    u.ans = max({l.ans, l.sum + r.le, r.ans});
}
 

void build(ll u, ll l, ll r){
    if(l == r){
        tr[u] = {a[l], -INF, -a[l]};
        return;
    }
    ll mid = (l + r) >> 1, tl = u << 1, rs = u << 1 | 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    push_up(tr[u], tr[tl], tr[rs]);
}
void modify(ll l, ll r, ll i, ll x, ll d){
	if(l == r){
        tr[i].ans = -1e18;
        tr[i].sum = d;
        tr[i].le = -d;
        return;
    }
    int mid = l + r >> 1, tl = i << 1, rs = i << 1 | 1;
    if(x <= mid)
        modify(l, mid, tl, x, d);
    else 
        modify(mid + 1, r, rs, x, d);
    push_up(tr[i], tr[tl], tr[rs]);
}
segtree query(ll l, ll r, ll i, ll L, ll R){
    if(L <= l && r <= R)
        return tr[i];
    ll mid = l + r >> 1, tl = i << 1, rs = i << 1 | 1;
    if(mid >= R)
        return query(l, mid, tl, L, R);
    else if(mid < L)
        return query(mid + 1, r, rs, L, R);
    else {
        segtree ltre = query(l, mid, tl, L, R), rtre = query(mid + 1, r, rs, L, R), nowtre;
        push_up(nowtre, ltre, rtre);
        return nowtre;
    }
}

void solve(){
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    build(1, 1, n);
    while(m--){
        int op, l, r;
        cin >> op >> l >> r;
        if(op == 1){
            modify(1, n, 1, l, r);
        }
        else
            cout << query(1, n, 1, l, r).ans << endl;
    }
}

调了好久呜呜呜,modify函数一直出问题爆内存,差点调崩溃掉(...

  • 35
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值