Codeforces Round #702 (Div. 3)

58 篇文章 0 订阅
13 篇文章 1 订阅

A. Dense Array

题意:一个数组,要求相邻两个数不能相差两倍以上。问需要填入多少个数,使得满足条件。

思路:找到不满足的地方,然后每次填入min的两倍。直到满足。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int c;
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 0 ; i < n ; i ++) cin>>a[i];
        //sort(a,a+n);
        int res = 0;
        for(int i = 1 ; i < n ; i ++){
            int minn = min(a[i],a[i-1]);
            int maxx = max(a[i],a[i-1]);
            while(minn*2 < maxx){
                minn *= 2;
                res++;
            }
        }
        cout<<res<<endl;
    }
    return 0;
}

B. Balanced Remainders

题意:一个数组长度是3的倍数。要使得所有元素对3取模,余数是0 1 2 的个数相同。可以对数组元素进行+1操作。 问最少操作多少次。

思路:毫无疑问先对3取模先。然后分情况讨论。对于cnt0。 如果cnt0大于n/3。那么多出来的哪些一定要转移到1上去。此时如果cnt1 也大于 n/3 ,那么转移到2上去就好了。 如果cnt1 不大于 n/3 那么 cnt2必然大于 n/3,那么久用cnt2的转移过来就好了。对于 cnt1和 cnt2 一样考虑就好了。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int c;
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n;
        int cnt[3] = {0};
        int num = n/3;
        for(int i = 0 ; i < n ; i ++){
            cin>>a[i];
            cnt[a[i]%3] ++;
        }
        //cout<<cnt[0]<<" "<<cnt[1]<<" "<<cnt[2]<<endl;
        int res = 0;
        if(cnt[0] > num){
            res += cnt[0]-num;
            cnt[1] += cnt[0]-num;
            if(cnt[1] > num){
                res += cnt[1]-num;
            }else{
                res += 2*(cnt[2]-num);
            }
        }
        else if(cnt[1] > num){
            res += cnt[1]-num;
            cnt[2] += cnt[1]-num;
            if(cnt[2] > num){
                res += cnt[2]-num;
            }else{
                res += 2*(cnt[0]-num);
            }
 
        }
        else if(cnt[2] > num){
            res += cnt[2]-num;
            cnt[0] += cnt[2]-num;
            if(cnt[0] > num){
                res += cnt[0]-num;
            }else{
                res += 2*(cnt[1]-num);
            }
        }
        cout<<res<<endl;
    }
    return 0;
}

C. Sum of Cubes

题意:问一个整数能否拆成两个数的立方和。即 x = a3+b3

思路:因为数据比较小。x < 1e12 ,那么a最大只能到 1e4。 所以可以标记所有的a3。 然后 枚举 b3。 判断a3是否标记过就好了。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int c;
map<int,int> mp;
 
signed main(){
    cin>>t;
    for(int i = 1 ; i <= 10000 ; i ++){
        mp[i*i*i] = 1;
    }
    while(t--){
        cin>>n;
        int res =0 ;
        for(int i = 1 ; i*i*i < n ; i ++){
            if(mp[n-i*i*i]){
                res = 1;
                break;
            }
        }
        puts(res ? "YES" :"NO");
    }
    return 0;
}

D. Permutation Transformation

题意:给定一个排列。表示一颗二叉树。根节点是数组的最大值。左边的是左子树,右边的是右子树。左右子树同样按照根节点的逻辑构造。求所有点的深度。

思路:这不就是中序遍历的二叉树。dfs解决。每次找当前区间的最大值。然后分成左右两个区间,递归。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int c;
int dep;
int res[N];
 
void dfs(int l,int r,int depp){
    if(l >= r) return ;
    int maxi = l;
    for(int i = l; i < r ; i ++){
        if(a[i] > a[maxi]) maxi = i;
    }
    res[maxi] = depp;
    dfs(l,maxi,depp+1);
    dfs(maxi+1,r,depp+1);
}
 
signed main(){
    cin>>t;
    while(t--){
        cin>>n;
        for(int i = 0; i < n ; i ++) cin>>a[i];
        dfs(0,n,0);
        for(int i = 0 ; i < n ; i ++){
            cout<<res[i]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

E. Accidental Victory

题意:n个人。每个人有 ai个代币。两两pk,代币多者通吃,拿走败者的代币。如果相同,则等概率胜负。问有多少人可能是最后赢家。即吃掉了所有人的代币。

思路:最大值肯定是可以成为赢家的。然后,任何一个人是可以吃掉所有比他小的人的。他的代币数量就是这些的前缀和。如果这个前缀和比后面一个人大。那就可以继续吃。直到前缀和都没后一个人大。那前面这些人不可能成为最后赢家。从后往前遍历一遍就行了。找到第一个不符合条件的位置。后面的肯定都是符合的。前面的都不符合。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
//int a[N],b[N];
int c;
int dep;
int sum[N];
struct node{
    int id,x;
}a[N];
bool cmp(node a1,node a2){
    if(a1.x == a2.x)
        return a1.id < a2.id;
    return a1.x < a2.x;
 
}
map<int,int> mp;
signed main(){
    cin>>t;
    while(t--){
        mp.clear();
        cin>>n;
        for(int i = 0; i < n ; i ++){
            cin>>a[i].x;
            a[i].id =  i;
        }
        sort(a,a+n,cmp);
        sum[0] = a[0].x;
        for(int i = 1 ; i < n ; i ++){
            sum[i] = a[i].x+sum[i-1];
        }
        a[n].x = 0;
        for(int i = n-1 ; i >= 0 ; i --){
            if(sum[i] < a[i+1].x){
                break;
            }
            mp[a[i].id] = 1;
        }
        vector<int> res;
        for(int i = 0 ; i < n ; i ++){
            if(mp[i]){
                res.push_back(i+1);
            }
        }
        cout<<res.size()<<endl;
        for(auto i : res){
            cout<<i<<" ";
        }
        cout<<endl;
    }
    return 0;
}

F. Equalize the Array

题意:给定一个数组。要求删掉一些数。使得数组中各个值的出现次数相同。问最少删掉多少。

思路:最少删掉多少,也就是求最多留下多少。统计每个数的出现次数。按出现次数从大到小排序。那么可以留下的就是 max(cnt[i]*i)。相当于求下图最大长方形的面积。

在这里插入图片描述

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
//int a[N],b[N];
int c;
int dep;
int sum[N];
int a[N];
map<int,int> mp;
vector<int> vt;
int b[N];
 
signed main(){
    cin>>t;
    while(t--){
        mp.clear();
        vt.clear();
        cin>>n;
        for(int i = 0; i < n ; i ++){
            cin>>a[i];
            if(mp[a[i]] == 0){
                vt.push_back(a[i]);
            }
            mp[a[i]] ++;
        }
        m = vt.size();
        for(int i = 0 ; i < m ; i ++){
            b[i] = mp[vt[i]];
        }
        sort(b,b+m);
        reverse(b,b+m);
        int res = 0;
        for(int i = 0 ; i < m ; i ++){
            res = max(b[i]*(i+1),res);
        }
        cout<<n-res<<endl;
    }
    return 0;
}

G. Old Floppy Drive

题意:给定一个长度为n的数组。给定一个x。数组是循环的。连续取数。也就是取完最后一个数,又从头开始取。直到取出的所有数的和,不小于x。问取了多少个数。 可能永远取不到满足条件的。输出-1。

思路:对数组做一下前缀和。然后只留下前缀和的递增序列。因为肯定是希望取到的数越取越大。如果变小了,那肯定不会在这个位置停下来。然后分情况讨论。 第一种情况。 x 小于等于 前缀和的最大值。那么最多取一遍数,就可以满足条件了。如果x大于前缀和。那因为可以循环取嘛。可以多取一些数。但是,如果 整个数组的和是负的。那么多取几遍只会负的越来越多。不可能。反之,因为前缀和中有一个最大值。 然后就看,去掉这个最大值之后,剩下的,需要取几轮。取完这些,再最后取一个满足条件的位置停就好了。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#include <unordered_map>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 10007;
const int N = 2e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int c;
int dep;
 
vector<int> vt;
vector<int> vt2;
 
signed main(){
    cin>>t;
    while(t--){
        vt.clear();
        vt2.clear();
        cin>>n>>m;
        int sum = 0;
        for(int i = 0 ; i < n ; i ++){
            cin>>a[i];
            sum += a[i];
            if(i == 0){
                vt.push_back(sum);
                vt2.push_back(i);
            }else if(sum > vt[vt.size()-1]){
                vt.push_back(sum);
                vt2.push_back(i);
            }
        }
        int sz = vt.size();
        vector <int> res;
        for(int i = 0 ; i < m ; i ++){
            int x;
            cin>>x;
            int pos = lower_bound(vt.begin(),vt.end(),x)-vt.begin();
            if(pos >= sz){
                if(sum <= 0)
                    res.push_back(-1);
                else{
                    int now = (x-vt[vt.size()-1]+sum-1)/sum;
                    int rem = x-now*sum;
                    if(rem == 0){
                        res.push_back(now*n-1);
                        continue;
                    }
                    pos = lower_bound(vt.begin(),vt.end(),rem)-vt.begin();
                    res.push_back(now*n+vt2[pos]);
                }
            }else{
                res.push_back(vt2[pos]);
            }
        }
        for(auto i : res){
            cout<<i<<" ";
        }
        cout<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值