Codeforces Round #521

A题
res = a*(k - k/2) - b*(k/2)
注意用long long
B 题
改变最小的a,使得序列中不存在1 0 1的情况。
扫描一遍,遇到101的时候,把后面一个1给翻转成0,然后记录一下次数输出。

C 题
题意,一个序列如果其中N-1个数的和与另一个数相等,那么该序列是good
给你一个序列,多少中方法,可以删除一个aj,使得这个序列为good

思路:
直接考虑枚举即可,注意记录每个数的位置,排序后,记录前N-1个数的和,以及最后一个数。从1开始扫描,判断删除这个数后,该序列是否为good,最后注意再判断一下删除第N个元素后是否符合。

代码如下:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5+10;
typedef long long ll;
class Node{
public:
    ll v;
    int p;
};
int N;
Node q[MAX];
bool cmp(Node A,Node B){
    return A.v < B.v;
}
int main(void){
    scanf("%d",&N);
    for(int i=1;i<=N;++i){
        scanf("%lld",&q[i].v);
        q[i].p = i;
    }
    sort(q+1,q+1+N,cmp);
    ll sum = 0,Max = q[N].v;
    for(int i = 1;i<=N-1;++i){
        sum += q[i].v;
    }
    vector<int> vec;
    for(int i=1;i<=N-1;++i){
        if(sum - q[i].v == Max){
            vec.push_back(q[i].p);
        }
    }
    if(sum - q[N-1].v == q[N-1].v)
        vec.push_back(q[N].p);
    printf("%d\n",(int)vec.size());
    for(int i=0;i<vec.size();++i){
        printf("%d",vec[i]);
        if(i != vec.size()-1)
            printf(" ");
        else
            printf("\n");
    }
    return 0;
}

D题
题意:给你N个数的序列,每次删除K个数,要求能删除的次数最多,问你这个K个数是什么?

思路:
最开始是考虑选择出现最多的K个数,但看了样例2 发现如果某个数出现次数很多,那么选择多次这个数会更好。所以要把每个数能贡献的次数考虑进去。我的做法是二分最小的出现次数,使得这最小出现次数尽量大。check函数是判断当前这个最小次数v能否筹够K 个数,每个数能贡献的是 出现次数/v,因为每个数可以被放进多个。

具体代码 如下:

#include<bits/stdc++.h>

using namespace std;
const int MAX = 2e5+10;
typedef long long ll;
int s;
int N,K;
int book[MAX];
vector<int> vec[MAX];//该出现次数有哪些数。
void init(){
    for(int i=1;i<=(int)2e5;++i){
        if(book[i] != 0){
            vec[book[i]].push_back(i);
        }
    }
}
bool check(int v){
    ll sum = 0;
    for(ll i=v;i<=N;++i){
        sum += i/v*(int)vec[i].size();
        //对于该出现次数,贡献出现次数/v*(个数)
    }
    if(sum >= K)    return true;
    else    return false;
}
int main(void){
    scanf("%d%d",&N,&K);
    for(int i=1;i<=N;++i){
        scanf("%d",&s);
        book[s]++;
    }
    init();
    int l = 1,r = N;
    while(r - l > 1){
        int mid = (l+r)/2;
        if(check(mid))
            l = mid;
        else
            r = mid;
    }
    if(check(r))    l = r;
    vector<int> res;
    for(int i=l;i<=N;++i){
        int Time = i/l;//Time表示这个数应该被放入多少次。
        //把高于该出现次数的数都放进去即可。
        for(int j=0;j<vec[i].size();++j){
            for(int k=0;k<Time;++k){
                res.push_back(vec[i][j]);
            }
        }
    }
    for(int i=0;i<K;++i){
        printf("%d",res[i]);
        if(i != K-1)
            printf(" ");
        else
            printf("\n");
    }
    return 0;
}

E题
题意:相当求的次数序列,比如1 1 3 3 5 5 5 次数序列就是2 2 3,即数的出现次数。从中可以得到的序列
x,2x,4x,符合每一个数是前面的二倍,
这里每个数可以由比本身大的数组成,比如4,可以由比4大的数。问你最大的该序列和是多少。似乎有点乱。。。具体读题吧。

做法是,直接枚举x即可。把所有次数放到multiset里面,判断比2x大的次数是否具有,如果有就继续判断4x直到停止。
x从1枚举到N即可。因为每次判断最多是log级,所以总的时间复杂度也就O(Nlog2(N));

代码如下:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
map<int,int> mp;
multiset<int> st;
int N,x;
int main(void){
    scanf("%d",&N);
    for(int i=1;i<=N;++i){
       scanf("%d",&x);
       mp[x]++;
    }
    for(map<int,int>::iterator it = mp.begin();it != mp.end();++it){
        st.insert(it->second);
    }
    ll res = 0;
    for(int i=1;i<=N;++i){
        int now = i;
        ll temp = 0;
        multiset<int>::iterator it = st.lower_bound(now);
        while(true){
            if(it == st.end())  break;
            temp += now;
            now *= 2;
            if(*it >= now)  it++;
            else    it = st.lower_bound(now);
        }
        res = max(res,temp);
    }
    printf("%lld\n",res);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值