Codeforces Round 935 (Div. 3) A - E 题解

A. Setting up Camp

题意:给你a,b,c,a表示独居的人的个数,b表示可以同居的人的个数,但是必须要一屋子三个人,c表示可以两个人一起住也可以三个人一起住的人数,问最少需要几间屋子?
题解:先看a,a有多少人就需要多少间屋子,再看b,如果c的人数无法和b凑成3的倍数,那么一定是无解,其他的情况就是让c和b凑出最大的3的倍数,剩下的c最多组成一个屋子,得解。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 7 ;
const int mod = 998244353 ;
inline ll read() {
	ll x = 0, f = 1 ;
	char c = getchar() ;
	while (c > '9' || c < '0') {
		if (c == '-')
			f = -1 ;
		c = getchar() ;
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0' ;
		c = getchar() ;
	}
	return x * f ;
}

bool vis[maxn] ;
ll t , n , d , k , a[maxn] , b[maxn] , sum[maxn] , Sum[maxn] ;
void solve(){
	n = read() ;
	d = read() ;
	k = read() ;
	ll ans = n ;
	ll res = (d + 3 - 1) / 3 ;
	res = res * 3 ;
	res = res - d ;
	if(k >= res){
		ll Ans = (k + d) / 3 ;
		Ans = Ans * 3 - d ;
//		cout << Ans << endl ;
		cout << ans + ((k + d) / 3ll) + ((k - Ans) > 0 ? 1 : 0) << endl ;
		return ;
	}
	else{
		cout << -1 << endl ;
	}
}
int main(){
	t = read() ;
	while(t --){
		solve() ;
	}
	return 0 ;
}

B. Fireworks

题意:给你a , b , c ,一共有两种烟花,第一种每隔a分钟发射一次,第二种每隔b分钟发射一次,问找到一个区间满足x<=i <= x + c,问找到一个区间能同时看到最多的烟花数量是多少?
题解:很明显,从lcm(a , b)开始,然后再看c分钟能放多少个烟花即可,答案就是2 + (c / a) + (c / b)
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 7 ;
const int mod = 998244353 ;
inline ll read() {
	ll x = 0, f = 1 ;
	char c = getchar() ;
	while (c > '9' || c < '0') {
		if (c == '-')
			f = -1 ;
		c = getchar() ;
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0' ;
		c = getchar() ;
	}
	return x * f ;
}

bool vis[maxn] ;
ll t , n , d , k , a[maxn] , b[maxn] , sum[maxn] , Sum[maxn] ;
void solve(){
	n = read() ;
	d = read() ;
	k = read() ;
	cout << 2ll + (k / n) + (k / d) << endl ;
}
int main(){
	t = read() ;
	while(t --){
		solve() ;
	}
	return 0 ;
}

C. Left and Right Houses

题意:给你一个字符串包含0和1,要建设一条公路在x屋子之后,1表示该屋子主人想在公路的右边居住,0表示该屋子主人想在公路的左边居住,公路尽量建设在中心地段,问道路左边满足的人数和道路右边满足的人数都大于该区段的x/2上取整的坐标是多少,如果答案相同,取小坐标。
题解:看到n个数据范围n <= 300000,那么很容易想到贪心,先将坐标设置在0点,统计出人数,然后每次向右移动,如果说为1,那么右边的人数就减一,如果是0,那么左边的人数就加一,统计出所有满足的答案,然后再找到离市中心最近的坐标尽量小的答案即可。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 7 ;
const int mod = 998244353 ;
inline ll read() {
	ll x = 0, f = 1 ;
	char c = getchar() ;
	while (c > '9' || c < '0') {
		if (c == '-')
			f = -1 ;
		c = getchar() ;
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0' ;
		c = getchar() ;
	}
	return x * f ;
}

bool vis[maxn] ;
char s[maxn] ;
ll t , n , d , k , a[maxn]  , sum[maxn] , Sum[maxn] ;
double b[maxn] ;
void solve(){
	int cnt = 0 ;
	n = read() ;
	scanf("%s" , s + 1) ;
	ll zuo = 0 , you = 0 ;
	for(int i = 1 ; i <= n ; i ++){
		if(s[i] == '1'){
			you ++ ;
		}
	}
	if(you >= (n + 2 - 1) / 2){
		b[++ cnt] = 0 ;
	}
	for(int i = 1 ; i <= n ; i ++){
		if(s[i] == '1'){
			you -- ;
		}
		if(s[i] == '0'){
			zuo ++ ;
		}
		ll res = i ;
		ll Res = (n - i) ;
		if(zuo >= (res + 2 - 1) / 2 && you >= (Res + 2 - 1) / 2){
			b[++ cnt] = i ;
		}
	}
//	for(int i = 1 ; i <= cnt ; i ++){
//		cout << b[i] << " " ;
//	}
//	cout << endl ;
	if(n % 2 == 1){
		double mid = n / 2.0 ;
		ll rt = n ;
		double Min = INT_MAX ;
		for(int i = 1 ; i <= cnt ; i ++){
			double res = abs(b[i] - mid) ;
			if(res < Min){
				Min = res ;
				rt = b[i] ;
			}
			if(res == Min){
				rt = min(rt , (ll)b[i]) ;
			}
		}
		cout << rt << endl ;
	}
	else{
		double mid = (n / 2.0) ;
		ll rt = n ;
		double Min = INT_MAX ;
		for(int i = 1 ; i <= cnt ; i ++){
			double res = abs(b[i] - mid) ;
			if(res < Min){
				Min = res ;
				rt = b[i] ;
			}
			if(res == Min){
				rt = min(rt , (ll)b[i]) ;
			}
		}
		cout << rt << endl ;
	}
}
int main(){
	t = read() ;
	while(t --){
		solve() ;
	}
	return 0 ;
}

D. Seraphim the Owl

题意:假设你是David,你是后来的,但是前面有n个人,每个人有两个属性a_ib_i,假设你当前的位置是i,你可以和1 <= j < i交换,但是你需要花费a[j] + \sum b[x]( j + 1<=x <=i),你最后最大的位置是k,问你最小的花费是多少?
题解:这很明显是个贪心问题,在k之后的人你选择min(a_i , b_i)即可,到k的时候,你就得需要注意,如果说当前的b_k + a[k - 1] < a[k]的,那么就可以继续往前取,我就在这里wa了一发!所以只需要注意最后再从k枚举到2,看看最小的答案是多少,累加即可。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 7 ;
const int mod = 998244353 ;
inline ll read() {
	ll x = 0, f = 1 ;
	char c = getchar() ;
	while (c > '9' || c < '0') {
		if (c == '-')
			f = -1 ;
		c = getchar() ;
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0' ;
		c = getchar() ;
	}
	return x * f ;
}

bool vis[maxn] ;
char s[maxn] ;
ll t , n , d , k , a[maxn]  , Sum[maxn] , b[maxn] , c[maxn] ;
void solve(){
	int cnt = 0 ;
	n = read() ;
	k = read() ;
	for(int i = 1 ; i <= n ; i ++){
		a[i] = read() ;
	}
	for(int i = 1 ; i <= n ; i ++){
		b[i] = read() ;
	}
	for(int i = 1 ; i <= n ; i ++){
		if(a[i] == b[i]){
			c[i] = 1 ;
		}
		if(a[i] > b[i]){
			c[i] = 1 ;
		}
		if(a[i] < b[i]){
			c[i] = 0 ;
		}
	}
	ll sum = 0 , ans = 0 ;
	for(int i = n ; i >= 1 ; i --){
		if(i == k){
			ll Ans = sum + a[i] ;
			for(int j = k ; j >= 2 ; j --){
				if(sum + b[j] + a[j - 1] < Ans){
					Ans = sum + b[j] + a[j - 1] ;
				}
				sum = sum + b[j] ;
			}
			ans = ans + Ans ;
			break ;
		}
		if(c[i] == 1){
			sum += b[i] ;
		}
		else{
			ans = ans + sum + a[i] ;
			sum = 0 ;
		}
	}
	cout << ans << endl ;
}
int main(){
	t = read() ;
	while(t --){
		solve() ;
	}
	return 0 ;
}

E. Binary Search

题意:给你一个序列,满足所有数字出现一次且不重复,给你一个数字k,你需要用二分查找来使最后的a[l] == k,二分查找的过程是这样的,先让l = 1 , r = n + 1, 每次取x = (l + r) / 2,如果a[x] <= k,那么l =x,要不然,r=x。你可以有两次修改数组的机会,swap(a[i] , a[j]),问操作是什么,不需要考虑最小次数。
题解:这种题一般都是有通解的,我们发现,假设第一次的中间值也就是a[(n + 2) / 2)]的值不是k,那么直接进行二分查找找到的最后的值和k值所在的位置交换,就可以满足题意,那如果说a[(n + 2) / 2] == k怎么办,其实也很简单,将这个值换到坐标n就可以了,就可以满足答案。
代码:
#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 1e6 + 7 ;
const int mod = 998244353 ;
inline ll read() {
	ll x = 0, f = 1 ;
	char c = getchar() ;
	while (c > '9' || c < '0') {
		if (c == '-')
			f = -1 ;
		c = getchar() ;
	}
	while (c >= '0' && c <= '9') {
		x = x * 10 + c - '0' ;
		c = getchar() ;
	}
	return x * f ;
}

bool vis[maxn] ;
char s[maxn] ;
ll t , n , d , k , a[maxn] , Sum[maxn] , b[maxn] , c[maxn] ;
void solve(){
	n = read() ;
	k = read() ;
	vector < pair < ll , ll > > q ;
	for(int i = 1 ; i <= n ; i ++){
		a[i] = read() ;
	}
	for(int i = 1 ; i <= n ; i ++){
		b[a[i]] = i ;
	}
	if(b[k] != n){
		q.push_back({b[k] , n}) ;
		swap(a[b[k]] , a[n]) ;
	}
	ll l = 1 , r = n + 1 ;
	while(r - l != 1){
		ll mid = (l + r) / 2ll ;
		if(a[mid] <= k){
			l = mid ;
		}
		else{
			r = mid ;
		}
	}
	if(a[l] != k){
		q.push_back({l , n}) ;
	}
	cout << q.size() << endl ;
	for(auto it : q){
		cout << it.first << " " << it.second << endl ;
	}
}
int main(){
	t = read() ;
	while(t --){
		solve() ;
	}
	return 0 ;
}

喜欢作者的记得点赞收藏加关注哦~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小许愿瓶

我的成长我做主

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值