Codeforces Round 902 (Div. 2) (CF1877) B、C、D 题解

B

题目大意

你要传话给 n 个人,每传一下话需要花费 p ,当一个人被传话后,他可以最多传给 ai 个人,每次花费 bi 。问把话传给 n 个人的最小花费。

分析

首先传给第一个人只少要 p

下来贪心,每次让花费最小、且能够传话的人去传话。

考虑建一个堆,堆内的信息是花费 x 和能传的次数 y ,堆顶是最小的花费 x ,然后进行如下的 n 次操作:

  • 取出堆顶,进行一次传话,其 y 减一
  • y 变为 0 ,则将其从堆中弹出

一共会进行 n 次操作,复杂度 O(n log n)

Code (注意开 C++ 20)

#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(false); cin.tie(0)
using namespace std;
using ll = long long;
int n, p;
void solve() {
    map<ll, ll> cnt;
    cin >> n >> p;
    vector<ll> a(n + 1), b(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        cnt[b[i]] += a[i];
    }
    cnt[p] += n;
    ll ans = p;
    for (int i = 1; i < n; i++) {
        auto [x, y] = *cnt.begin();
        ans += x;
        if (y == 1)
            cnt.erase(cnt.begin());
        else
            cnt[x]--;
    }
    cout << ans << endl;
}

int main() {
    IO;
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

C

题目大意

要求你构造一个长度为 n+1 的数组,你可以给 an+1 赋一个 [0,m] 之间的值,随后 i[1,n], ai=ai+1 mod i

问最后数组中有 k 个不同数的方案数

分析

注意到无论赋的这个值 an+1 有多大,下一个数 an 一定满足 an<n ,且 a1=0

假设 an+1=x+tn ,那么数组的情况如下:

i 1 2 x n n+1
ai 0 0 0 0 x x x+tn

所以数组最多存在 3 种不同的数字 ,当 k>3ans=0

下面分情况讨论:

  • k=1 ,那么 an+1=x+tnans=1

  • k=2 ,有两种情况:

    • an+1an mod n ,即 an+1<n ,总共有 min(n1,m)
    • an+10 mod n ,那么构造出来的数组是 i[1,n], ai=0, an+1=x ,总共有 nm
  • k=3 ,答案是 m

Code

#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(false); cin.tie(0)
using namespace std;

void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    if (k >= 4) {
        cout << 0 << endl;
    } else if (k == 3) {
        if (m < n) cout << 0 << endl;
        else cout << m - n - m / n + 1 << endl;
    } else if (k == 2) {
        if (m <= n) cout << m << endl;
        else cout << n + m / n - 1 << endl;
    } else {
        cout << 1 << endl;
    }
}

int main() {
    IO;
    int T;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

D

题目大意

给出 n 个点的值,选择将一些点染成黑色,只少染一个。如果染黑了点 i ,那么 i 的所有倍数的点会被染成绿色。

每次染色后,答案是被染色的点的最大值

一共有 2n1 种染色方式,问所有染色方式的答案之和。

分析

考虑每一个点对整个答案的贡献:

首先这个点需要是所有选中点里最大的,才会产生贡献。

换句话说,只有比这个点大的点都都\已经被计算完毕,并且排除掉后,它才会产生贡献。

那么考虑将点的值排序,从大往小考虑:

如果当前点 i 是最大的点,比 i 大的点已经被排除在外了,还剩下 k 个点,也就是点集 {i1,i2,,ik}

  • 假设这剩下的 k 个点中,是 ai 的因数的点有 l 个,点集为 {j1,j2,,jl} ,其中 i{j1,j2,,jl}

    那么 i 这个点产生的贡献值为 ai×(2k1+2k2++2kl)

  • 随后我们将属于 ai 因数的点集 {j1,j2,,jl}{i1,i2,,ik} 中删掉,继续考虑剩下的点中最大的那个数

下来考虑如何知道属于点 i 的因数有哪些?以及这些点有没有在考虑 i 之前被考虑到:

我的方法是建立一张图,如果点 u 是点 v 的倍数,那么建立一条 uv 边,建图的复杂度是 O(nn)

每次考虑点 i 就在这张图上从 i 顺着往下找。

Code

#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(false); cin.tie(0)
using namespace std;
using ll = long long;
using pll = pair<ll, ll>;
const ll mod = 998244353;
const int N = 1e5 + 5;
int n, cnt;
pll a[N];
ll ans;
vector<int> G[N];
ll qpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1) res = res * x % mod;
        y >>= 1;
        x = x * x % mod;
    }
    return res;
}
bool vis[N];
void init() {
    for (int i = 1; i * i <= n; i++) {
        for (int j = i * i; j <= n; j++) {
            if (j % i == 0) {
                G[j].push_back(i);
                if (j != i * i && i != 1)
                    G[j].push_back(j / i);
            }
        }
    }
}
void dfs(int now, ll val) {
    cnt--;
    ans += qpow(2, cnt) * val;
    ans %= mod;
    for (int son : G[now]) {
        if (vis[son]) continue;
        vis[son] = true;
        dfs(son, val);
    }
}

int main() {
    IO;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].first;
        a[i].second = i;
    }
    sort(a + 1, a + n + 1);
    cnt = n;
    init();
    for (int i = n; i >= 1 && cnt > 0; i--) {
        if (vis[a[i].second]) continue;
        vis[a[i].second] = true;
        dfs(a[i].second, a[i].first);
    }
    cout << ans;
    return 0;
}
posted @   过_路_人  阅读(97)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示