CF Round 794(div3)A-D题解

点这里进入比赛

A. Robin Helps

题目及范围:

思路:直接模拟题目操作。钱超过k,就让累加进罗宾汉的钱袋里。如果遇到没钱的人,并且自己有钱,就赏一块钱。否则就跳过。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#define int long long
#define endl '\n'
using namespace std;
int n,k,m,t;
int num[200];
 
 
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
	while(t--){
		int n,k;
		cin>>n>>k;
		int ans=0;
		int cnt=0;
		for(int i=1;i<=n;i++) cin>>num[i];
		for(int i=1;i<=n;i++){
			if(num[i]>=k){
				cnt+=num[i];
			} else if(num[i]==0){
				if(cnt>=1){//加判断条件,如果手里没钱就不接济穷人
					cnt--;
					ans++;
				}
			}
		}
		cout<<ans<<endl;
	}
    return 0;
}

B. Robin Hood and the Major Oak

题目及数据范围:

思路:注意到如果i是偶数,i^{i}必然为偶数。反之则必为奇数。依此化简题目。

最后的叶子数,就是最后k年的叶子的数量。同时我们易得生长的叶子数量必是一年奇数一年偶数的。那问题就变成了,求区间内长奇数片叶子的年份,是奇数还是偶数。因为无论k内长偶数片叶子的年份有几年,都是偶数。而奇数片叶子的年份如果是奇数,则总答案为奇数(奇+奇+奇=奇)。若为偶数年,总答案为偶数(奇+奇=偶)。总之就是求k年内长奇数片叶子的年份数量是奇还是偶就好啦。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
#define endl '\n'
using namespace std;
int n,k;
int t;
 
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>k;
        int cnt=0;//奇数年的数量。
        int l=max(1ll,n-k+1);//判断对答案有用的起始年。
        if(l&1)//起始年是奇数年
            if(k&1) cnt=k/2+1;//分长度为奇数和偶数分别求cnt。
            else cnt=k/2;
        else cnt=k/2;//起始年是偶数年,则cnt必等于k/2。
        if(cnt&1) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
    return 0;
}

C. Robin Hood in Town

题目与数据范围:

思路:有超过一半的人的钱乘上两倍还比平均的钱少,罗宾汉就会出现。

首先,如果只有一个人或者两个人,不可能存在上述情况。所以直接输出-1即可。

既然提到“一半以上”我们可以先排序,找钱严格大于所有人的一半的第一个人。我们叫他mid。

我们的目标是让mid也不开心,这样在他前面的一半人保证也不开心。

我们这个人的钱数乘以2,得到“临界状态下的平均钱数”。

有了平均钱数,乘以人数n,得到“临界状态的总数”,叫它ans。

如果现在的钱的总和tot已经超过这个值,则不用加x就会满足情况,所以输出0。

否则,临界值与现在总和的差值,就是要加上的x的大小。

(注意:因为要严格大于num[mid],所以ans要再加上1,保证mid会不开心)

复杂度瓶颈在排序,总复杂度O\left ( nlogn \right )

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
#define endl '\n'
using namespace std;
int n,k;
int t;
int num[200005];
 
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n;
        int tot=0;//这个变量存原先数组的总和。
        for(int i=1;i<=n;i++) cin>>num[i],tot+=num[i];
        sort(num+1,num+1+n);//升序排序。
        int midd=(1+n+1)>>1;//找mid的位置。
        int ans=num[midd]*2;
        ans*=n;//求目标值
        if(n<=2) cout<<-1<<endl;
        else if(tot>ans) cout<<0<<endl;
        else cout<<ans+1-tot<<endl;//记得要+1。
    }
    return 0;
}

D. Robert Hood and Mrs Hood

题目及数据范围:

思路:每个任务的执行时间都是一个区间,我们很容易想到用差分数组实现区间修改,最后知道每一天有几个任务。

但实际上,一个工作能影响的时间,不只是l到r这几天。还有l前面的几天和r后面的几天。因为一次访问是持续d天的。只要有一天重叠,就被影响到了。

设开始日为 x ,如果开始日 x 满足 l_{i}-d+1\leqslant x\leqslant r_{i} ,那么这段工作将与访问重叠。因为访问是持续d天的。(最后一天与工作重叠也算)

因此我们这里差分的不是这个工作开始到结束的区间。意思是,起始时间在 [l_{i}-d+1,r_{i}] 内的一个访问,都会被这个工作所影响到。所以我们差分的范围要更大一些。

之后,我们再遍历一遍原数组,枚举起始日,找到工作贡献最多和工作贡献最少的那一天,就是答案。

如果l_{i}-d+1< 0 , 就让贡献影响日期改成1,防止溢出。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstring>
#include <cmath>
#define int long long
#define endl '\n'
using namespace std;
int n,d,k,t;
int num[200005];

signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n>>d>>k;
        for(int i=1;i<=k;i++){
            int l,r;
            cin>>l>>r;
            l=max(1ll,l-d+1);//注意这里的l的意义不是任务的起始日期,是开始计算贡献的那一天。
            num[l]++;num[r+1]--;//差分数组给区间增加贡献
        }
        for(int i=1;i<=n;i++) num[i]+=num[i-1];//前缀和还原成原数组
        int p1=1;//妈妈来最合适的那一天
        int p2=1;//兄弟来最合适的那一天
        int maxx=-0xFFFFFFFFF;
        int minn=0xFFFFFFFFF;
        for(int i=1;i<=n-d+1;i++){//注意i的取值范围。i枚举的是开始访问的那一天
            if(num[i]>maxx) p1=i,maxx=num[i];//更新找最大最小
            if(num[i]<minn) p2=i,minn=num[i];
        }
        cout<<p1<<" "<<p2<<endl;
        for(int i=0;i<=n+1;i++) num[i]=0;//多测一定要清空,不然会不幸。(((
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值