zoukankan      html  css  js  c++  java
  • Codeforces Round #702 (Div. 3) 题解

    写在前边

    链接:Codeforces Round #702 (Div. 3)
    比较简单,但是总是感觉脑子有点转不过弯来。

    A. Dense Array

    链接:A题链接

    题目大意:

    在数组中插入若干个数,使得(cfrac{max(a[i], a[i + 1])}{min(a[i], a[i + 1])} leq 2),问至少需要插入多少个数。

    思路

    既然是相邻,那么只需要顺序模拟即可,每次把(min(a[i], a[i + 1])×2), 直到它的两倍大于等于(2)停止,求累计次数。

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    
    using namespace std;
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    const int N = 55;
    
    void solve() {
        int n;
        int a[N];
        scanf("%d", &n);
        for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    
        int res = 0;
        for (int i = 1; i < n; i++) {
            if (max(a[i], a[i - 1]) > 2 * min(a[i], a[i - 1])) {
                int m = min(a[i], a[i - 1]);
                while (m * 2 < max(a[i], a[i - 1])) {
                    res++;
                    m *= 2;
                } 
            }
        }
        printf("%d
    ", res);
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t); 
        while (t--) {
            solve();
        }
        return 0;
    }
    

    B. Balanced Remainders

    链接:B题链接

    题目大意:

    给定一个有(n)(n可以被3整除)个元素的数组,(c_0,c_1,c_2)分别表示数组中元素模(3)后的余数为(0, 1, 2)的数的个数,现在每次操作可以使一个数加(1),问最少经过几次操作可以使得(c_0 == c_1 == c_2)

    思路

    可以发现,(c_0, c_1, c_2)之间可以进行单一方向的转移,例如模(3)余数为(0)的数加(1)后余数变为(1),余数为(1)的数加(1)后余数变为(2),余数为(2)的数加(1)后余数变为(0)
    ,于是问题就转化成了转移多少次的问题,相邻的转移又一定是最优的。

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    #include <unordered_map>
    
    using namespace std;
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    const int N = 3e4 + 10;
    int a[N];
    
    void solve() {
        int n;
        scanf("%d", &n);
        unordered_map<int, int> hash;
        int cnt0 = 0, cnt1 = 0, cnt2 = 0;
        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
            hash[a[i] % 3]++;
        }
    
        int aver = n / 3;
        int res = 0;
        while (1) {
            if (hash[1] == hash[2] && hash[1] == hash[0] && hash[0] == hash[2]) {
                break;
            }
            if (hash[1] < aver) {
                hash[0] -= (aver - hash[1]);
                res += (aver - hash[1]);
                hash[1] = aver;
            }
            if (hash[2] < aver) {
                hash[1] -= (aver - hash[2]);
                res += (aver - hash[2]);
                hash[2] = aver;
            }
            if (hash[0] < aver) {
                hash[2] -= (aver - hash[0]);
                res += (aver - hash[0]);
                hash[0] = aver;
            }
        }
        printf("%d
    ", res);
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t);
        while (t--) {
            solve();
        }
        return 0;
    }
    

    C. Sum of Cubes

    链接:C题链接

    题目大意:

    判断一个数(x)是否满足(x = a^3 + b^3 \, (a, b geq 1))

    思路

    因为数据最大为(10^{12}),所以(a,b in [1, 10^4)),所以一种做法是预处理出(a^3),然后再(O(10^4))枚举(b)(O(1))查找(a^3)
    另一种方法是(O(10^4))枚举(a),二分(b),复杂度(O(10^4 * log 10^4))

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    
    using namespace std;
    
    #pragma GCC optimize(2)
    #pragma GCC optimize(3,"Ofast","inline")
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    void solve() {
        LL x;
        scanf("%lld", &x);
        LL t1;
        for (LL i = 1; i <= 10000; i++) {
            LL l = 1, r = 10000;
            while (l < r) {
                LL mid = l + r >> 1;
                LL sum = i * i * i + mid * mid * mid;
                if (sum > x) r = mid;
                else if (sum < x) l = mid + 1;
                else {
                    puts("YES");
                    return;
                } 
            }
        }
        puts("NO");
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t); 
        while (t--) {
            solve();
        }
        return 0;
    }
    

    D. Permutation Transformation

    链接:D题链接

    题目大意:

    给定一个数组,用其数据建一棵树,最大的数始终作为树根,树根左侧数构成左子树,右边的数构成右子树,同理选大的作为子树的根,求每个数在树中所处的深度。

    思路

    (DFS)

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    
    using namespace std;
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    const int N = 110;
    int a[N];
    int res[N];
    int n;
    
    void build(int l, int r, int depth) {
        if (l > r) return;
    
        int idx = l;
        for (int i = l; i <= r; i++) {
            if (a[i] > a[idx]) idx = i;
        }
        res[idx] = depth;
        build(l, idx - 1, depth + 1);
        build(idx + 1, r, depth + 1);
    }  
    
    void solve() {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    
        build(0, n - 1, 0);
    
        for (int i = 0; i < n; i++) printf("%d ", res[i]);
        puts("");
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t); 
        while (t--) {
            solve();
        }
        return 0;
    }
    

    E. Accidental Victory

    链接:E题链接

    题目大意:

    (n)个玩家,每人都有一个点数,两个人对战,点数大的获胜,相同点数随机一人获胜,获胜后获得败者的点数,问有哪些人会有获胜的可能性。

    思路

    一个人要想获胜,那么肯定需要干掉所有的人才可以,首先按照点数给人排序,我们发现对于第i个人它可以直接获得它之前人的所有点数,这里可以用前缀和处理,如果可以赢得比赛,那么他后边的人也一定能赢得比赛,因为他后边的人可以获得更多的点数,所以可以发现有二段性,因此我们可以直接二分出第一个获胜的人,剩下的直接输出即可。

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    
    using namespace std;
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    #define score first
    #define num second
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    const int N = 2e5 + 10;
    LL b[N];
    int n;
    PII a[N];
    
    bool check(int x) {
        LL t = b[x];
        for (int i = x + 1; i <= n; i++) {
            if (t >= a[i].score) {
                t += (LL) a[i].score;
                continue;
            }
            else return false;
        }
        return true;
    }
    
    void solve() {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i].score);
            a[i].second = i;
        }
        sort(a + 1, a + n + 1);
    
        for (int i = 1; i <= n; i++) b[i] = b[i - 1] + (LL) a[i].score;
        int l = 1, r = n;
        while (l < r) {
            int mid = l + r >> 1;
            if (check(mid)) r = mid;
            else l = mid + 1; 
        }
        vector<int> res;
        for (int i = l; i <= n; i++) res.push_back(a[i].num);
        sort(res.begin(), res.end());
    
        printf("%d
    ", n - l + 1);
        for (auto &it : res) printf("%d ", it);
        puts("");
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t); 
        while (t--) {
            solve();
        }
    
        return 0;
    }
    

    F. Equalize the Array

    链接:F题链接

    题目大意:

    (cnt_x)为数组中每个数的出现次数,如果一个数组中的数只有(C)次或者(0)次可以认为这个数组是漂亮的,所以我们现在要做的就是删掉某些数使得这个数组是漂亮的。

    思路

    按照题意,(cnt_x)小于(C)(x)那么全部删除,大于(C)的需要删除(cnt_x - C)个,所以有:

    [删除个数 = sumlimits_{cnt_x < C} cnt_x + sumlimits_{cnt_x >= C} (cnt_x - C) ]

    看到这个公式能想到什么,当然是前缀和。
    但是有一个问题,(C)的范围一定是某个数的出现次数吗,如果不是,那么就没法用前缀和做了,那怎么证明呢,如果(C)不是某个数的出现次数,我们令某个刚好大于(C)的次数为(Y),可以发现,(sumlimits_{cnt_x < C} cnt_x)没有发生变化,而大于(sumlimits_{cnt_x >= C} (cnt_x - C) > sumlimits_{cnt_x >= Y} (cnt_x - Y)),这样于是删除个数变大了,所以我们根本不需要考虑如果(C)不是某个数的出现次数这种情况。

    有了前缀和(presum),根据公式,左边就有(left = presum[i - 1]), 右边就有(right = (presum[last] - presum[i - 1]) - (last - i + 1) * cnt[i]),于是更新答案即可。

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    
    using namespace std;
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    const int N = 2e5 + 10;
    int n, c;
    LL presum[N];
    
    bool cmp(int a, int b) {
        return a > b;
    }
    
    void solve() {
        scanf("%d", &n);
        map<LL, LL> mp;
        for (int i = 0; i < n; i++) {
            scanf("%d", &c);
            mp[c]++;
        }
        
        vector<LL> cnt; //计数 数字出现的次数 
        for (auto &it : mp) cnt.push_back(it.second);
        cnt.push_back(0);
        sort(cnt.begin(), cnt.end()); 
        
        LL res = n;
        int last = cnt.size() - 1;
        for (int i = 1; i < cnt.size(); i++) presum[i] = presum[i - 1] + cnt[i];
        for (int i = 1; i <= last; i++) {
            LL left = presum[i - 1], right = (presum[last] - presum[i - 1]) - (last - i + 1) * cnt[i];
            res = min(res, left + right);
        }
        printf("%d
    ", res);
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t); 
        while (t--) {
            solve();
        }
    
        return 0;
    }
    

    G - Old Floppy Drive

    链接:G题链接

    题目大意:

    现在有个含有(n)个元素的数组(a),且有一个指针指向(a)的初始,还有一个(m)个元素的数组(x),对于每一个(x_i),指针会不停的运动,如果数组末尾那么它会重新指会第一个元素继续运动,直到指针走过的数组所有元素的和(≥x),那么指针停止,求指针的运动次数。

    思路

    设数组总和为(S),前缀和为(preSum),运行一次的时间是(T),那么运行(t)秒,那么走过的总和(Sum)

    [Sum = lfloor cfrac{t}{T} floor * S + preSum \, [t \, mod \, T] ]

    由此可见,如果(S leq 0) 并且 (maxlimits_{i = 1}^n \, preSum[i] < x)那么指针将永远运行下去不会停止,那么直接输出(-1)即可。

    然后就分别求磁盘转动的整圈数,然后再加上(maxlimits_{i = 1}^n \, preSum[i] geq x)的最小位置(i),可以知道,对于磁盘的转动整圈数不得少于(lceil frac{x - maxlimits_{i = 1}^n \, preSum[i] \, }{S} ceil),少于转不到,多于的话也就不能保证答案是最小位置了。

    最后就需要找到(maxlimits_{i = 1}^n \, preSum[i] geq x)的位置(i)了,可以用二分,注意这个前缀和并不是随随便便的前缀和,处理它的时候保证它是单调递增的且(>0),如果小于等于(0)那么对总和(Sum)没有任何贡献,并且单调递增才能保证我们可以得到那个刚好(maxlimits_{i = 1}^n \, preSum[i] geq x)的位置(i),而这一步我们可以用二分lower_bound寻找即可。

    代码

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <cstring>
    
    using namespace std;
    
    #define Inf 0x3f3f3f3f
    #define PII pair<int, int>
    #define P2LL pair<long long, long long>
    #define endl '
    '
    
    typedef long long LL;
    typedef unsigned long long ULL;
    typedef vector<long long> VLL;
    typedef vector<int> VI;
    
    LL gcd(LL a, LL b) {
        return b ? gcd(b, a % b) : a;
    }
    
    const int N = 2e5 + 10;
    int n, m;
    LL presum[N];
    LL preInd[N]; //记录前缀的坐标
    
    void solve() {
        int idx = 0; //当前坐标
        LL allsum = 0, c;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; i++) {
            scanf("%lld", &c);
            allsum += c; 
            if (allsum > presum[idx]) {
                presum[++idx] = allsum;
                preInd[idx] = i;
            }
        }
    
        for (int i = 1; i <= m; i++) {
            LL x;
            scanf("%lld", &x);
            if (presum[idx] < x && allsum <= 0) {
                printf("%d ", -1);
                continue;
            }
            LL needspins = 0;
            if (presum[idx] < x) {
                needspins = (x - presum[idx] + allsum - 1) / allsum;
            }
            x -= needspins * allsum;
            LL q = lower_bound(presum + 1, presum + idx + 1, x) - presum;
            LL res = needspins * n + preInd[q] - 1; //因为前缀从1开始,那么减去1
            printf("%lld ", res);
        }
        puts("");
    }
    
    int main()
    {
        //ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
        int t;
        scanf("%d", &t);
        while (t--) {
            solve();
        }
    
        return 0;
    }
    
  • 相关阅读:
    codevs 2632 非常好友
    codevs 1213 解的个数
    codevs 2751 军训分批
    codevs 1519 过路费
    codevs 1503 愚蠢的宠物
    codevs 2639 约会计划
    codevs 3369 膜拜
    codevs 3135 River Hopscotch
    数论模板
    JXOJ 9.7 NOIP 放松模拟赛 总结
  • 原文地址:https://www.cnblogs.com/ZhengLijie/p/14418248.html
Copyright © 2011-2022 走看看