Codeforces Round 1042 (Div. 3) (A ~ E)

A. Lever

模拟

题意:

给定两个数组\(a\),\(b\)规定每次迭代执行以下两种操作

  • 随机选择\(i\)这样的索引\(a_i\) > \(b_i\)。然后将\(a_i\)减少\(1\)。如果不存在这样的\(i\),则忽略此步骤。

  • 随机选择\(i\)这样的索引\(a_i\) < \(b_i\)。然后将\(a_i\)增加\(1\)。如果不存在这样的\(i\),则忽略此步骤

每次迭代都会检查操作\(1\)是否执行如果被忽略则结束迭代,问迭代次数

思路:

迭代次数 = 计算操作\(1\)需要执行几次,所以只需对于所有满足\(a_i > b_i\)条件求两者之差再求和加\(1\)即可

代码:

void solve()
{
    int n;
    std::cin >> n;

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

    i64 maxv = 0;
    for(int i = 1; i <= n; i ++) 
        maxv += (a[i] > b[i] ? a[i] - b[i] : 0);
    
    std::cout << maxv + 1 << '\n';
}

B. Alternating Series

构造

题意:

规定满足以下两个条件的数组为好数组

  • 相邻元素的乘积为负

  • 对于所有长度至少为\(2\)的子数组,子数组中所有元素之和为正数

规定如果一个数组的词典长度比另外一个数组小,就说前一个数组是优于后者的

输出一个长度为\(n\)的好数组,使得它优于其他所有长度为\(n\)的好数组。

思路:

首先要保证词典长度最小,那么就要尽可能的填绝对值小的数,那么问题是先填\(1\)还是填\(-1\),考虑到所有长度大于等于\(2\)的子数组和都要大于\(0\)的条件,那么在保证词典长度最小的情况下肯定负数绝对值越小越有利,这样填的正数也不会太大,这样奇数位都填\(-1\)那偶数位我们最少能填多少,不难发现如果当前不是最后一位偶数位的话,填\(3\)才能保证子数组和是正数,最后一位偶数位填\(2\)就足够了

代码:

void solve()
{
    int n;
    std::cin >> n;

    for(int i = 1; i <= n; i ++)
    {
        if(i == n and i % 2 == 0) std::cout << 2 << ' ';
        else if(i & 1) std::cout << -1 << ' ';
        else std::cout << 3 << ' ';
    }
    std::cout << '\n';
}

C. Make it Equal

数论

题意:

给定两个多重集合\(S\)\(T\)以及一个整数\(k\),可以在\(S\)集合上执行任意次以下操作:

  • \(S\)中选择元素\(x\),并在\(S\)中删除\(x\)的一次出现。然后在\(S\)中插入\(x + k\)或在\(S\)中插入\(\left| x - k \right|\)判断能否通过以上操作可以得到集合\(T\)

思路:

\(x + k, x + 2 * k, x + 3 * k\)模数的结果为\(t1\)\(\left| x - k \right|\)\(k\)\(t2\),发现 \(t1 + t2 = k\)的,并且 \(t1\)\(t2\) 两类可以通过操作相互转换,所以记录集合\(S\)所有余数,然后遍历集合\(T\),看余数\(t1,t2\)够不够就可

代码:

void solve()
{
    int n, k;
    std::cin >> n >> k;

    std::map<int,int> mp;
    std::vector<int> s(n + 1),v(n + 1);
    for(int i = 1; i <= n; i ++) std::cin >> s[i];
    for(int i = 1; i <= n; i ++) std::cin >> v[i];

    for(int i = 1; i <= n; i ++) mp[s[i] % k] ++;

    for(int i = 1; i <= n; i ++)
    {
        if(mp[v[i] % k] > 0) mp[v[i] % k] --;
        else if(mp[k - (v[i] % k)] > 0) mp[k - (v[i] % k)] --;
        else 
        {
            std::cout << "NO" << '\n';
            return;
        }
    }

    std::cout << "YES" << '\n';
}

D. Arboris Contractio

贪心

题意:

给定一棵树,可以选一个起点和一个终点然后删掉两点之间简单路径上的所有边,然后把所有路径上的结点都跟起点连一条边,问最少需要操作多少次才能使这颗树的直径最小

思路:

不难发现树最小直径只能是\(1 or 2\),当\(n = 2\)的时候最小直径就是\(1\),其他情况都是\(2\)(也就是所有点都连接同一节点也就是所谓的菊花图)。自己画几个样例会发现每一个叶子节点都需要一次操作,所以先统计所有叶子节点的个数,然后我们需要找一个合适的根节点,什么样的根节点合适呢?我们发现根节点直接相连的叶子节点是不需要操作的,所以我们要找一个叶子节点最多的节点作为根节点,最后答案就是所有根节点数量 - 与根节点相连的叶子节点

代码:

void solve()
{
    int n;
    std::cin >> n;

    std::vector<std::vector<int>> adj(n + 1);
    for(int i = 1; i < n; i ++)
    {
        int u,v;
        std::cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    
    int res = 0;
    for(int i = 1; i <= n; i ++) 
        if(adj[i].size() == 1) res ++;

    if(res >= n - 1)
    {
        std::cout << 0 << '\n';
        return;
    }

    int cnt = 0;
    for(int i = 1; i <= n; i ++)
    {
        int ans = 0;
        for(auto v : adj[i])
            if(adj[v].size() == 1) ans ++;
        
        cnt = std::max(cnt,ans);
    }

    std::cout << res - cnt << '\n';
}

E. Adjacent XOR

贪心

题意:

给定两个数组\(a,b\),对于每个\(i\)都可以进行\(a_i = a_i \oplus a_{i + 1}\)最多一次的操作,问可不可以将\(a\)转换成\(b\)

思路:

首先对于\(a_n\)如果不等于\(b_n\)的话那就一定不能,从后往前遍历,可以发现对于每个\(a_i \ne b_i\)的情况,能提供给\(a_i\)操作的数只有两个\(a_i\)\(b_i\),所以只需维护两个数判断能不能将\(a_i\)转换成\(b_i\)即可

代码:

void solve()
{
    int n;
    std::cin >> n;

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

    std::vector<int> num(2);
    for(int i = n; i >= 1; i --)
    {
        if(i == n and a[i] != b[i])
        {
            std::cout << "NO" << '\n';
            return;
        }

        if(a[i] == b[i])
        {
            num[0] = num[1] = a[i];
            continue;
        }

        bool ok = false;
        for(auto x : num)
        {
            if((x ^ a[i]) == b[i])
            {
                ok = true;
                break;
            }
        }

        if(!ok)
        {
            std::cout << "NO" << '\n';
            return;
        }

        num[0] = a[i],num[1] = b[i];
    }

    std::cout << "YES" << '\n';
}
posted @ 2025-08-31 20:28  Gabriel_7  阅读(5)  评论(0)    收藏  举报