• Codeforces Round #552 (Div. 3) 题解


    Codeforces Round #552 (Div. 3) 题目链接

    A. Restoring Three Numbers

    给出 (a+b)(b+c)(a+c) 以及 (a+b+c) 这四个数,输出一种合法的 (a,b,c)

     
    可以发现,前面的两个数加起来减去最后的 (a+b+c),答案就出来一个。最后这样求出(a,b,c)即可。

    代码如下:

    Code ```cpp #include using namespace std; typedef long long ll; const int N = 2e5 + 5; ll a[5]; int main() { cin >> a[0] >> a[1] >> a[2] >> a[3]; sort(a, a + 4) ; cout << a[0] + a[1] - a[3] << ' ' << a[0] + a[2] - a[3] << ' ' <

    B. Make Them Equal

    给出(n)个数,然后选择一个最小的(D),现在你可以对于每一个(a_i)执行下面的操作:

    • (a_i + D)
    • (a_i - D)
    • (a_i)保持不变
      最后要使得所有的(a_i)都相等,如果不存在这样的 (D),则输出 (-1)

     
    排序后可以发现这(n)个数分为了几段,然后根据情况分类讨论就行了(贪下心)

    代码如下:

    Code ```C #include using namespace std ; typedef long long ll; const int N = 105; int a[N] ; int n; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; for(int i = 1; i <= n; i++) cin >> a[i] ; sort(a + 1, a + n + 1) ; int cnt = 0; for(int i = 1; i <= n; i++) if(a[i] != a[i - 1]) cnt++; if(cnt > 3) { cout << -1; return 0 ; } if(cnt == 1) { cout << 0; return 0; } if(cnt == 2) { int mn = a[1], mx = a[n] ; if((mx - mn) & 1) cout << mx - mn ; else cout << (mx - mn) / 2; } else { int d = -1; for(int i = 2; i <= n; i++) { if(a[i] != a[i - 1]) { if(d == -1) d = a[i] - a[i - 1]; else if(a[i] - a[i - 1] != d) d = -1; } } cout << d; } return 0; } ```

    C. Gourmet Cat

    现在有(a,b,c)三种食物,其中周一、周四、周日吃第一种食物,周二、周六吃第二种食物,其余天吃第三种食物。
    现在给出三种食物的数量,问最多可以吃多少天。

     
    每一周可以作为一个整的阶段,可以先贪心地考虑,先考虑三种食物最多可以吃多少周,然后取min即可得出周数。之后用食物数量减去7*周数,然后小范围暴力就行了。

    具体见代码吧:

    Code ```C #include using namespace std; typedef long long ll; const int N = 2e5 + 5; ll a[5] ; ll b[20] = {0, 1, 2, 3, 1, 3, 2, 1}; ll sum[5][20] ; int main() { cin >> a[1] >> a[2] >> a[3]; ll k = 1e18 ; for(int i = 8; i <= 14; i++) b[i] = b[i - 7]; for(int i = 1 ; i <= 14 ; i++) { sum[1][i] = sum[1][i - 1]; sum[2][i] = sum[2][i - 1]; sum[3][i] = sum[3][i - 1]; sum[b[i]][i] += 1; } for(int i = 1; i <= 3; i++) { ll p; if(i == 1) p = 3; else p = 2; k = min(k, a[i] / p); } for(int i = 1; i <= 3; i++) { ll p; if(i == 1) p = 3; else p = 2; a[i] -= k * p; } ll ans = k * 7 ; ll sum4 = 0; for(int i = 1; i <= 14; i++) { for(int j = 14; j >= i; j--) { int d1 = sum[1][j] - sum[1][i - 1]; int d2 = sum[2][j] - sum[2][i - 1]; int d3 = sum[3][j] - sum[3][i - 1]; if(d1 <= a[1] && d2 <= a[2] && d3 <= a[3]) { sum4 = max((ll)j - i + 1, sum4); } } } cout << ans + sum4; return 0 ; } ```

    D. Walking Robot

    有一个机器人,现在要从(0)号点走到(n)号点,现在有一定数量的(a)物品和(b)物品。机器人每次向前移动一步,若目标点(s_i=1),那么此时使用(a)物品,(b)物品的数量会加一(不会超过原本的值)。
    每次使用一个物品,那个物品的数量就会减一。
    现在问这个机器人最多走到哪个位置。

     
    这个就是简单的贪心,当(s_i=1)时,如果(b)物品的数量没有满,那么此时肯定选择(a)物品是最优的;其余情况,就尽可能地先选择(a)物品,毕竟(a)物品的数量可以增加。当然一个物品的数量为(0)时只能选择另外一个物品了,两个物品的数量为(0)时,说明机器人最多也只能走到这里了= =

    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n, a, b;
    int s[N];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> a >> b;
        for(int i = 1; i <= n; i++) cin >> s[i] ;
        int i;
        int mx = b;
        for(i = 1; i <= n; i++) {
            if(s[i] == 0) {
                if(b) b--;
                else if(a) a--;
                else break ;
            } else {
                if(a && b < mx) {
                    a--;
                    b++;
                    b = min(b, mx) ;
                } else if(b) {
                    b--;
                } else break ;
            }
        }
        cout << i - 1;
        return 0;
    }
    

    E. Two Teams

    给出(n)个数,数据保证这(n)个数互不相同,然后现在两个人轮流来进行操作:选择目前最大的那个数,然后删去以它为中心向两边延伸(k)个单位的数(若遇到边界就停止)。
    最后输出每一个数是由哪一个人删去的,两个人的编号分别为(1,2)

     
    双向链表模拟即可,找最大值的话,由于数据互不相同,所以可以用一个数组记录一下每个数的位置。
    当然直接set来搞暴力删除也可以。

    代码如下(比较丑陋):

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n, k, cur;
    int l[N], r[N], a[N], ans[N], b[N], mp[N];
    bool vis[N] ;
    void solve(int p) {
        int left, right ;
        ans[p] = cur + 1;
        vis[a[p]] = 1;
        int tmp = p;
        int cnt = 0;
        while(l[tmp] != 0 && cnt < k) {
            ans[tmp] = cur + 1;
            vis[a[tmp]] = 1;
            tmp = l[tmp] ;
            cnt++;
        }
        left = tmp ;
        ans[left] = cur + 1;
        vis[a[left]] = 1;
        tmp = p;
        cnt = 0;
        while(r[tmp] != n + 1 && cnt < k) {
            ans[tmp] = cur + 1;
            vis[a[tmp]] = 1;
            tmp = r[tmp] ;
            cnt++;
        }
        right = tmp ;
        ans[right] = cur + 1;
        vis[a[right]] = 1;
        l[r[right]] = l[left] ;
        r[l[left]] = r[right] ;
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0) ;
        cin >> n >> k;
        for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i], mp[a[i]] = i;
        for(int i = 1; i <= n; i++) {
            l[i] = i - 1;
            r[i] = i + 1;
        }
        sort(b + 1, b + n + 1) ;
        reverse(b + 1, b + n + 1) ;
        cur = 0;
        int point = 1;
        for(int i = 1; i <= n; i++) {
            while(point <= n && vis[b[point]]) point++;
            if(point > n) break ;
            int now = mp[b[point]] ;
            solve(now) ;
            cur ^= 1;
        }
        for(int i = 1; i <= n; i++) cout << ans[i];
        return 0;
    }
    

    F. Shovels Shop

    (n)个物品, 每个物品的价格为(a_i),现在你需要买(k)个物品。与此同时你还有(m)张优惠券,每张给出两个数("x_i,y_i"),含义为如果你一次性买了(x_i)个物品,那么在你买的物品中,最便宜的(y_i)个物品可以免费。
    现在问你买(k)个物品最少花多少钱。
    (1leq n leq 10^5,1leq kleq min(n,2000))

     
    这题我们首先考虑贪心,其实我们只需要在最便宜的(k)个物品中选择就可以了,这肯定不会丧失最优解。
    然后就是比较简单的dp了,设(dp(i))表示买前(i)个物品的最少花费,那么转移方程就为:

    [egin{equation} dp(i) = egin{cases} dp(i-1) + a_i,\ dp(i-x_i) + sum_{i-x_i} - sum_{i-x_i+y_i} end{cases} end{equation} ]

    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5 ;
    int dp[N] ;
    int n, m, k ;
    int a[N], sum[N];
    vector <pair<int,int> > p ;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m >> k ;
        for(int i = 1; i <= n; i++) cin >> a[i] ;
        sort(a + 1 , a + n + 1) ;
        for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i] ;
        n = min(n, k) ;
        for(int i = 1; i <= m; i++) {
            int x, y;
            cin >> x >> y;
            p.push_back(make_pair(x, y)) ;
        }
        for(int i = 1; i <= n; i++) {
            dp[i] = dp[i - 1] + a[i] ;
            for(auto v : p) {
                if(i >= v.first) dp[i] = min(dp[i], dp[i - v.first] + sum[i] - sum[i - v.first + v.second]) ;
            }
        }
        cout << dp[n] ;
        return 0;
    }
    

    G. Minimum Possible LCM

    给出(n)个数,其中(1 leq n leq 10^7),现在求满足(lcm(a_i,a_j))最小时的一组合法解((i,j))

     
    考虑稍微暴力一点的做法:因为有(lcm(a_i,a_j)=frac{a_i*a_j}{gcd(a_i,a_j)}),所以我们可以考虑枚举gcd,找到两个最小(a_i,a_j),满足它们都为gcd的倍数,不断维护答案即可。
    至于这样为什么是对的,假设现在找到的(a_i,a_j),有(gcd(a_i,a_j)=k*g,k>1),我们现在得到(ans_i)。之后我们枚举到(g=gcd(a_i,a_j))时,得到新的(ans),必然是满足(ans<ans_i)的。
    由于时限开的4s,加上cf机子也不错,还是很轻易跑过去的。最后注意下记录位置就好了。

    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e7 + 5;
    int n;
    int a[N], vis[N], pos[N][2];
    set <int> s;
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i] ;
            vis[a[i]]++;
            if(pos[a[i]][0] == 0) pos[a[i]][0] = i;
            else pos[a[i]][1] = i;
        }
        ll ans = 1e18;
        int b, c;
        for(ll i = 1; i < N; i++) {
            ll fir = -1, sec = -1;
            for(int j = i; j < N; j += i) {
                if(vis[j]) {
                    if(fir == -1) fir = j;
                    else if(sec == -1) sec = j;
                }
                if(vis[j] > 1) {
                    if(sec == -1) sec = j;
                }
                if(fir != -1 && sec != -1) {
                    if(fir * sec / i < ans) {
                        ans = fir * sec / i;
                        b = fir, c = sec ;
                    }
                    break ;
                }
            }
        }
        if(pos[b][0] == pos[c][0]) s.insert(pos[b][0]), s.insert(pos[c][1]) ;
        else s.insert(pos[b][0]), s.insert(pos[c][0]);
        for(auto v : s) cout << v << ' ' ;
        return 0;
    }
    
  • 相关阅读:
    闭包
    TCL
    [Go] gin框架渲染html字符串
    [Go] 使用packr包把静态文件打包进二进制内
    [javascript] 获取正则子表达式里的内容
    [redis] Zremrangebylex命令移除元素令人困惑不能理解
    [Go] 获取文件夹下面指定模式的文件列表 , 并且获取文件创建时间删除超过30分钟的文件
    [vuejs] 聊天框在overflow:auto中填数据时滚动到底部
    [vuejs] 在vuejs中使用websocket进行实时通讯
    Egret顶级开发者—李昌平
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10726443.html
Copyright © 2020-2023  润新知