Codeforces Round #781 (Div. 2) A—C

Codeforces Round #781 (Div. 2)

https://codeforces.com/contest/1665
我懒得写题意了,这场题目理解难度不大,构造难度大

A GCD vs LCM

思路

用 1 来构造(后面三个都是1,剩下的丢到第一个那里去
以后碰到LCM 和 GCD 的题目都可以往1去凑凑。

Code

#include <bits/stdc++.h>

using namespace std;

int main () {
    int t;
    cin >> t;
    while (t --) {
        int n;
        cin >> n;
        cout << n - 3 << " 1 1 1" << endl;

    }
}

B - Array Cloning Technique

思路

有点倍增的思想,就是每次都可以把序列当中的 最长相同数字 从其它数列里面给挪过来。
如果无法凑齐,就复制一遍,次数++
(我一开始以为每次复制都只能copy最原始的数列,所以错了)

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 1e5 + 5;

int main () {
    int t;
    cin >> t;
    while (t --) {
        int n, maxn = 0;
        cin >> n;
        map<int, int> mp;
        for (int i = 1; i <= n; i ++) {
            int x;
            cin >> x;
            mp[x] ++;
            maxn = max (maxn, mp[x]);
        }

        if (maxn == n) {
            cout << 0 << endl;
            continue;
        }

        int ans = 1;
        n -= maxn; //出去最大的外,有多少不同
        //先要copy一次
        ans += min (n, maxn); 
        n -= maxn; 
        
        while (n > 0) {
            maxn *= 2, ans ++; //copy
            ans += min (n, maxn); //剩下的 or 成倍复制
            n -= maxn; //copy
        } 
        
        cout << ans << endl;
    }
}
//找最长的那个,每次把两个它移进来
//注意是倍增的,不需要每两次一次
//每次复制后,取原排列中重复次数最多的数乘2到符合题意为止

C - Tree Infection

思路

有两种操作:

  1. Spreading: 把所有已经易染色的节点的兄弟节点同时染上色 (注意:染多个点只消耗一次操作,但是这多个点不可以互为兄弟)
  2. Injection: 对于任意一个节点进行染色
    由此观之,1.的效率更高,所以先来操作1.
    而操作顺序是,先染兄弟节点较多的,这也好理解(兄弟多的到了后面也会被染色,捎带上兄弟少的一起)
    具体操作细节请看Code及注释(已经写到让我这种苯人都能理解清楚的程度了)

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
int n, cnt, sib[N]; //sib[]兄弟节点
vector<int>v[N];

void init () {
    for (int i = 1; i <= n; i ++)
        v[i].clear();
    sib[1] = 0; //根节点没有兄弟
    cnt = 1; //根节点染色
}

bool check (int x) {
    int res = 0;
    for (int i = 1, j = x - 1; i <= cnt; i ++, j --)
        res += max (0, sib[i] - j); //有可能为负
    return x - cnt >= res; //x总操作次数,cnt是spreading的次数
}

int main () {
    int t; cin >> t;
    while (t --) {
        cin >> n;
        init(); //necessary
        
        for (int i = 2; i <= n; i ++) {
            int x;
            cin >> x; //输入父亲
            v[x].push_back (i); //记录所有的儿子
        }

        for (int i = 1; i <= n; i ++)
            if (!v[i].empty())
                sib[++ cnt] = v[i].size() - 1;
        sort (sib + 1, sib + cnt + 1, greater<int>());

        int l = cnt, r = n; //最多也就是每个点都染一次
        //最大值最小,二分
        while (l < r) {
            int mid = l + r >> 1;
            if (check (mid))
                r = mid; //求最少的次数
            else
                l = mid + 1;
        }

        cout << r << endl;
    }
}
posted @ 2022-04-28 23:15  Sakana~  阅读(36)  评论(3编辑  收藏  举报