Codeforces Round #694 (Div. 2)

A

  这个题目就是说有一个序列\(a\),并且给我们一个除数\(x\),我们可以对序列\(a\)相邻的两项进行合并(也就是用这两个数的和数来替换掉原来的两个数),我们要求出\(\sum \lceil \frac{a_i}{x} \rceil\)的最大值和最小值。
思路:
  最大值很显然就是对原来的序列不做任何的合并,这样原来序列中的所有数的贡献并不会被抵消所以\(max = \sum_{i = 1} ^ {n} \lceil \frac{a_i}{x} \rceil\),最小值是要将\(a_i\)所产生的贡献尽可能的抵消掉,但是我们只能将相邻的两项相加,所以要使最后的贡献最小,就是将所有的数都加起来保证能够尽可能大的抵消掉所有数对最后结果的贡献,\(min = \lceil \frac{\sum a_i}{x} \rceil\)

int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; i ++ ) std::cin >> a[i];

    i64 ans = 0;
    for (int i = 1; i <= n; i ++ ) ans += a[i] / m, a[i] %= m;
    i64 sum = std::accumulate(all(a), 0ll);
    i64 mx = ans + sum / m + !(sum % m == 0);
    rep(i, 1, n + 1) ans += (a[i] > 0);
    
    std::cout << mx << " " << ans << "\n";

B

  题目就是给我们一个序列\(a\)每一次在这个序列\(a\)后面增加\(x\)\(\frac{a_i}{x}\),一直到\(a_i\)不能整除\(x\)为止,求\(\sum a_i\)
思路:
  很显然每一次在序列后面加的数的和就是被分解的\(a_i\),所以我们只需要统计序列\(a\)中被分解次数最小的那个数的位置\(idx\)和次数\(cnt\),最后求和的时候,在\(idx\)左边的所有数产生的贡献和就是\(\sum_{0}^{idx-1} a_{i} * (cnt+1)\),在\(idx-1\)右边的所有数的贡献就是\(sum_{idx}^{n-1} a_{i} * cnt\);

void solve() {
    int n, x;
    std::cin >> n >> x;
    std::vector<int> a(n);
    rep(i, 0, n) std::cin >> a[i];

    i64 ans = 0;
    std::vector<int> cnt(n);
    rep(i, 0, n) {
        int t = a[i];
        while (t % x == 0) {
            cnt[i] ++;
            t /= x;
        }
    }
    int idx = 0, mn = 1e9;
    rep(i, 0, n) 
        if (mn > cnt[i]) idx = i, mn = cnt[i];

    mn ++;
    rep(i, 0, n) 
        if (i < idx) ans += 1ll * a[i] * (mn + 1);
        else ans += 1ll * a[i] * mn;
    
    std::cout << ans << "\n";
}

C

  题目就是说给\(n\)个人买礼物,给第\(i\)个人买的礼物只能是\(k_i\)之前的礼物,但是所有的\(m\)个礼物最多只能够买一次,如果不能买礼物的话,可以给第\(k_i\)个物品所对应的价格\(c[k_i]\),让我们求出他给朋友准备礼物花的最少的钱是多少。
思路:
  很明显我们要让总钱数最小的话,就必须让本来对应的\(c[k_i]\)大的收到\(k_i\)之前的礼物,所以我们先将\(k_i\)进行排序,将价格小的物品优先给序号靠后的人,由于每一个礼物只能买给一个人,所以我们可以用一个下标\(idx\)来记录当前送到了第几个礼物,当\(c[k_i] < c[idx]\)的时候,我们就开始直接送钱,因为题目中保证了\(c_1 \leq c_2 \leq \dots \leq c_m\) 所以这样的分配一定是最优的。

void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> k(n), c(m);
    rep(i, 0, n) std::cin >> k[i];
    rep(i, 0, m) std::cin >> c[i];
    std::sort(all(k));
    
    i64 ans = 0;
    int idx = 0;
    std::vector<bool> st(n);
    
    per(i, 0, n) 
        if (c[k[i] - 1] > c[idx]) ans += c[idx ++ ];
        else ans += c[k[i] - 1];
    std::cout << ans << "\n";
}

D

  该题目告诉我们两个数\(x,y\)它们的\(\frac{lcm(x, y)}{gcd(x, y)}\)如果是一个完全平方数的话,我们就称它们是相邻的。 现在给我们一个长度为\(n\)的数组\(a\),每一秒数组中的每个元素\(a_i\)都被数组中与当前值相邻的所有元素(包括其自身)的乘积所取代。让\(d_i\)是每一个\(a_i\)的相邻的值,一个数组的美丽度被定义为\(max_{1\leq i \leq n} d_i\)。现在有\(q\)次询问,每次都会让我们求出来\(w\)秒后这个数组的美丽度是多少。


思路:
  看到\(\frac{lcm(x,y)}{gcd(x, y)}\)是一个完全平方数,可以想着去推一下式子,因为\(lcm(x, y) = \frac{x * y}{gcd(x, y}\),所以可以将原式推导为\(\frac{x * y}{(gcd(x, y)) ^ {2}}\) 是一个完全平方数,所以可以写成\((\frac{\sqrt{x * y}}{gcd(x, y)}) ^ 2 == x ^ 2\)当且仅当\(x * y\)是完全平方数的时候该等式方才成立。那么问题就转化成了将\(x * y\)分解质因数,其中每一个因子的幂次都可以被\(2\)整除也就是\(x * y = p_{1} ^ {k_1} * p_{2} ^ {k_2} \dots * p_{n} ^ {k_n}\),其中\(k_1,k_2,k_3 \dots k_n\)都是\(2\)的倍数。因此我们只需要记录\(z_i = p_{1} ^ {k_1\%2} * p_{2} ^ {k_2 \% 2} \dots * p_{n} ^ {k_n \% 2}\) ,如果\(z_i == z_j\)说明两数相乘的质因子的幂次都为偶数,是个完全平方数。
  用欧拉筛来得到每一个数的最小质因子

void Eular(int n) {
    prime[idx ++ ] = 2;
    minp[2] = 2;
    st[2] = true;
    for (int i = 3; i <= n; i += 2) {
        if (!st[i]) prime[idx ++ ] = i, minp[i] = i, st[i] = true;
        for (int j = 0; prime[j] * i <= n; j ++ ) {
            st[prime[j] * i] = true;
            minp[prime[j] * i] = prime[j];
            if (i % prime[j] == 0) break;
        }
    }
}

在我们得到\(z_i\)的时候如果出现了偶数次幂,那么我们后面在判断完全平方数的时候还是会被分解掉,所以我们只需要在出现奇数次幂的时候在\(z_i\)的基础上乘上一个\(p_{i} ^ {1}\)

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    rep(i, 1, n + 1) std::cin >> a[i];
    std::map<int, int> mp;
    rep(i, 1, n + 1) {
        int val = a[i];
        int z = 1;
        while (val > 1) {
            int p = minp[val];
            int cnt = 0;
            while (val % p == 0) {
                cnt ++;
                val /= p;
            }
            if (cnt % 2 == 1) z *= p;
        }
        mp[z] ++;
    } // 找到匹配上的另一个数

    int cnt0 = 0;
    for (auto &p : mp) cnt0 = std::max(cnt0, p.second);
    for (auto &p : mp) {
        if (p.first == 1 || p.second % 2 == 1) continue;
        mp[1] += p.second;
        mp[p.first] = 0;
    }

    int cnt1 = 0;
    for (auto &p : mp) cnt1 = std::max(cnt1, p.second);
    int m;
    std::cin >> m;
    while (m -- ) {
        int op;std::cin >> op;
        std::cout << (op == 0 ? cnt0 : cnt1) << "\n";
    }
}
posted @ 2022-04-14 02:26  浅渊  阅读(45)  评论(0)    收藏  举报