Codeforces Round #673 (Div. 2)

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

A. Copy-paste

题意:一个数组,可以选择任意一个值加上另外一个值,当数组中有数大于k时停止。问,最多总共能操作多少次。

思路:大水题,排序,选最小的一直加就好了

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
int n,k;
int a[N];
 
 
int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>k;
        for(int i = 0; i < n ; i  ++) cin>>a[i];
        sort(a,a+n);
        int tmp  = a[0];
        int res = 0;
        int flag = 1;
        for(int i = 1 ; i < n ; i ++){
            if(a[i] > k){
                flag = 0;
                break;
            }
            res += (k-a[i])/tmp;
        }
        if(flag) cout<<res<<endl;
        else cout<<0<<endl;
    }
    return 0;
}

B. Two Arrays

题意:一个数组,然后要把这些数分成两组,如果组内有两个数加起来 == T,cost就+1,现在要求怎么分组,可以使得cost最小。

思路:只要考虑两种情况,a+b == T,如果a == b,并且数组中有n个a,那就两组分均分,是最优的。如果a != b,那么把a,b分到不同的两组去就好了,cost为0。显然是最优的。代码可以更简洁的,被我写烂了。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int n,k;
int a[N];
map<int,int> mp;
map<int,int> res;
struct node{
    int x;
    int id;
}b[N];
 
bool cmp(node a1,node a2){
    return a1.id <a2.id;
}
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        mp.clear();
        res.clear();
        cin>>n>>k;
        for (int i = 0 ; i < n ; i  ++) cin>>a[i];
        for (int i = 0 ; i < n ; i ++) {
            mp[a[i]] ++;
            b[i].id = i;
            b[i].x = a[i];
        }
        for(int i = 0 ; i < n ; i ++){
            if(res[b[i].x]){
                continue;
            }
            if(mp[k-b[i].x]){
                mp[b[i].x] = 0;
                res[b[i].x] = 1;
            }else{
                mp[b[i].x] = 0;
                res[b[i].x] = 2;
            }
 
        }
        for(int i = 0 ; i < n ; i ++){
            cout<<res[a[i]]-1<<" ";
            if(a[i]+a[i] == k){
                res[a[i]] = res[a[i]] == 2 ? 1 :2;
            }
        }
        cout<<endl;
    }
    return 0;
}

C. k-Amazing Numbers

题意:一个数组,给定一个k值时,找出 在所有长度为k的子区间中都出现过的最小值。现在要找出k值[1-k]的所有解。

思路:思考这样一个问题,如果一个数x,出现在了所有长度为k的子区间,那么数组中所有x之间的距离都小于等于k。这样才能确保用k去框子区间时,每次都能框到这个数。考虑两边的数。如果,x只出现了一次,那么要确保从以左右两边为端点的子区间都能够框到他。x出现了多次,也是一样,要确保左右两边都能够框到x。然后呢。如果对于区间长度为k,x每次都出现了,那么对于,k+1,k+2,k+3。。。那就更能框到这个数了。所以本题就是求解,对于x来说,k最小为多少,可以确保每次都框到他。把最小k值相等的数放在同一个容器,然后排序,就是长度为k时,能框到的最少的数。然后从小到大遍历k,k小的时候能框到的数,随着k变大,也是一直可以框到的。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int n,k;
int a[N];
map<int,int> mp;
int minn[N];
int cnt[N];
int last[N];
vector<int> vt[N];
int res[N];
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        mp.clear();
        for(int i = 1 ; i <= n ; i  ++){
            vt[i].clear();
            cnt[i] = 0;
            mp[i] = 0;
            minn[i] = 0;
            last[i] = 0;
        }
        for(int i = 1 ; i <= n ; i  ++){
            cin>>a[i];
            cnt[a[i]] ++;
            minn[a[i]] = n-i+1;
        }
        for(int i = 1 ; i <= n ; i ++){
            if(cnt[a[i]] == 1){
                minn[a[i]] = max(minn[a[i]],i);
                continue;
            }
            if(!mp[a[i]]){
                minn[a[i]] = max(minn[a[i]],i);
                mp[a[i]] = i;
            }else{
                minn[a[i]] = max(minn[a[i]],i-mp[a[i]]);
                mp[a[i]] = i;
            }
        }
        for(int i = 1 ; i <= n ; i ++){
            vt[minn[a[i]]].push_back(a[i]);
        }
        for(int i = 1 ; i <= n ; i ++){
            if(vt[i].size() == 0){
                res[i] = -1;
                continue;
            }
            sort(vt[i].begin(),vt[i].end());
            res[i] = vt[i][0];
        }
        for(int i = 2 ; i <= n ; i ++){
            if(res[i-1] != -1){
                if(res[i] != -1){
                    res[i] = min(res[i],res[i-1]);
                }else{
                    res[i] = res[i-1];
                }
            }
        }
        for(int i = 1 ; i <= n ; i ++) cout<<res[i]<<" ";
        cout<<endl;
    }
    return 0;
}

D. Make Them Equal

题意:一个数组,可以进行一种操作,ai:=ai−x⋅i , aj:=aj+x⋅i。问,怎么在3n次操作之内,把所有元素变成相同的。

思路:看一眼这个公式,可以发现,数组里面的值的总和是不变的, 所以先求和,判断是不是n的倍数。如果不是就无解。反之有解。有解的话,要求在3n步以内,那么可以先把所有的数都累加到a[1]上去。然后再从a[1]分发到各个位置。但是要把所有数加到位置1,必须要 a[i]能够整除i才行,如果不整除呢。如果不整除,就先用a[1]的值拿过来补一些,补到整除,然后再全部给a[1]。因为所有元素是大于等于1,的所以位置2上的数一定可以回收成功。那么回收了位置2的数,a[1] >= 2,那么就可以回收a[3],同理,往后一直回收,把所有数集中到a[1],位置,然后o(n),给每个位置分配平均数就行了。

AC代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int n,k;
int a[N];
struct node{
    int i,j,w;
    node(int a,int b,int c){
        i = a;
        j = b;
        w = c;
    }
};
vector<node> vt;
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        vt.clear();
        cin>>n;
        int sum = 0 ;
        for(int i = 1 ; i <= n ; i ++){
            cin>>a[i];
            sum += a[i];
        }
        if(sum%n != 0){
            cout<<-1<<endl;
            continue;
        }
        int ave = sum/n;
        //cout<<sum<<" "<<ave<<endl;
        for(int i = 2; i <= n; i ++){
            if(a[i]%i){
                vt.push_back(node(1,i,i-a[i]%i));
            }
            vt.push_back(node(i,1,(a[i]+i-1)/i));
        }
        for(int i = 2; i <= n; i ++){
            vt.push_back(node(1,i,ave));
        }
        cout<<vt.size()<<endl;
        for(int i = 0 ; i < vt.size() ; i ++){
            cout<<vt[i].i<<" "<<vt[i].j<<" "<<vt[i].w<<endl;
        }
    }
    return 0;
}

E. XOR Inverse

题意:一个数组。要找到一个x。使得数组中每个数都异或x之后。数组中的逆序对数量最少。

思路:这种一般就是贪心构造。应且又和异或有关。那肯定和字典树有关。就往这个方向去想。首先用字典树,可以求出数组中逆序对的数了。dp[i][j] 表示第i位上 如果为j。存在的逆序对数量。考虑第i位上的0和1两颗子树。如果现在插进去的是0。那么逆序对是不是增加了 sz[i][1]。并且这个逆序对数量是当i位为1时产生的。所以 dp[i][1] += sz[i][1]。同理插入0时。 dp[i][0] += sz[i][0]。然后在构造x的时候。肯定选择逆序对更少的。 如果 dp[i][1] > dp[i][0] 那么就要让这一位变成0。这样就留下了 dp[i][0] 所以答案加上 (1<<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 = 1e9+7;
const int N = 5e6+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int sz[N];
int tree[N][2];
int tot = 0;
int dp[N][2];
void insert(int x){
    int pos = 0;
    for(int i = 30 ; i >= 0 ; i --){
        int tmp = (x >> i ) & 1;
        if(!tree[pos][tmp]) tree[pos][tmp] = ++tot;
        dp[i][tmp^1] += sz[tree[pos][tmp^1]];
        pos = tree[pos][tmp];
        sz[pos] ++;
    }
}

signed main(){
    cin>>n;
    for(int i = 0 ; i < n;  i ++){
        int x;cin>>x;
        insert(x);
    }
    int res = 0,cnt = 0;;
    for(int i = 30 ; i >= 0 ; i--){
        if(dp[i][1] > dp[i][0]) res += (1LL<<i),cnt += dp[i][0];
        else cnt += dp[i][1];
    }
    cout<<cnt<<" "<<res<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值