Codeforces Round 859 (Div. 4) 题解集


比赛链接

CF1807A Plus or Minus

题目链接

Description

给定 a , b , c a,b,c a,b,c 三个整数。

  • a + b = c a+b=c a+b=c,输出 +
  • a − b = c a-b=c ab=c,输出 -

保证三个数的关系仅有上述两种。

Solution

判断两种情况即可。

分支结构题单

循环结构题单

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
int a,b,c;
int main(){
	cin>>t;
	while(t--){ //循环结构
		cin>>a>>b>>c;
		if(a+b==c) cout<<"+"<<endl; //分支结构
		else cout<<"-"<<endl;
	}
	return 0;
}

CF1807B Grab the Candies

题目链接

Description

n n n 包糖果,每包有 a i a_i ai 糖果,如果该包糖果数是偶数,Mihai会拿走,否则 Bianca会拿走。

两人会按照顺序依次拿走 a 1 , a 2 … a n a_1,a_2\dots a_n a1,a2an,现在可以对数列重新排序,问是否存在一种情况,使得任意时刻 Mihai拿到的糖果数严格大于 Bianca拿到的。

Solution

我们可以将所有偶数排在数列前面,这样开始 k k k k k k 为偶数的个数)包,Bianca的个数始终为零,最后 Bianca的个数为所有奇数之和(此时 Bianca的个数最大),与 Mihai的偶数之和比较即可。

即可转为奇数之和与偶数之和的大小比较。

注意,需严格大于(即两人数量相等是不行的)。

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
ll read(){
    ll x=0,f=1;
    char ch;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(){
	int n=read();
	int cnt1=0,cnt2=0;
	for(int i=1;i<=n;i++){
		int x=read();
		if(x%2==0)cnt1+=x;
		else cnt2+=x;
	}
	if(cnt1>cnt2) cout<<"YES"<<endl;
	else cout<<"NO"<<endl;
}
int main(){
	t=read();
	while(t--){
		solve();
	}
	return 0;
}

CF1807C Find and Replace

题目链接

Description

给定一串长度为 n n n 的字符串 s s s,你可以将每种字母转为 0 0 0 1 1 1,将所有字母转换完后,问能否得到一串由 0 0 0 1 1 1 交替而成的字符串(即相邻两个字符不同)。

Solution

因为最终得到的是 01交替的字符串,说明相同字符的下标相距定为偶数,而相同字母转换后又定为同一字符,所以我们枚举每种字母,看当前字母与上次该字母出现的位置的下标是否相距偶数个即可。

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
int last[30];
ll read(){
    ll x=0,f=1;
    char ch;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(){
	memset(last,-1,sizeof(last)); //last表示该字母上次出现的位置的下标,多测记得初始化
	int n=read();
	string s;
	cin>>s;
	for(int i=0;i<n;i++){
		int x=s[i]-'a'+1;
		if(last[x]!=-1){ //如果这不是该字母第一次出现
			if((i-last[x])%2==0){
				last[x]=i;
			}else{
				cout<<"NO"<<endl;
				return ;
			}
		}else last[x]=i;
	}
	cout<<"YES"<<endl;
}
int main(){
	t=read();
	while(t--){
		solve();
	}
	return 0;
}

CF1807D Odd Queries

题目链接

Description

对于长度为 n n n 的数列 a a a,有 q q q 次询问,每次给定 l , r , k l,r,k l,r,k,问将 a l , a l + 1 … a r a_l,a_{l+1}\dots a_r al,al+1ar 全都转为 k k k 后,该数列的总和是否为奇数。

注意,每次操作对后续操作没有影响,即每次操作都是在原数列 a a a 上做操作。

Solution

我们将每次操作完后分为两部分:操作部分和未操作部分。

操作部分总和为 ( r − l + 1 ) × k (r-l+1)\times k (rl+1)×k,未操作部分为 s u m n − ( s u m r − s u m l − 1 ) sum_n-(sum_r-sum_{l-1}) sumn(sumrsuml1),其中 s u m i sum_i sumi 表示前 i i i 个数的总和。

前缀和在输入时即可得到。

最后将两部分加起来,看是否为奇数即可。

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
ll sum[200200];
ll read(){
    ll x=0,f=1;
    char ch;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(){
	int n=read(),q=read();
	sum[0]=0;
	for(int i=1;i<=n;i++){
		sum[i]=0;
		int x=read();
		sum[i]=sum[i-1]+x;//求得前缀和
	}
	while(q--){
		int l=read(),r=read(),k=read();
		ll i=sum[n]-(sum[r]-sum[l-1]),j=k*(r-l+1); //分为两部分
		if((i+j)%2==0){
			cout<<"NO"<<endl;
		}else{
			cout<<"YES"<<endl;
		}
	}
}
int main(){
	t=read();
	while(t--){
		solve();
	}
	return 0;
}

CF1807E Interview

题目链接

Description

director n n n 堆石头,其中 n − 1 n-1 n1 堆都只有重量为一克的石头,剩下一堆有则有一块有两克的石头和若干重量为一克的石头。

你的任务是在 30 30 30 次询问内推理出那一堆有重量为两克的石头是第几堆。

首先输入 n n n,接下来输入 n n n 个数 a 1 , a 2 … a n a_1,a_2\dots a_n a1,a2an,其中 a i a_i ai 表示第 i i i 堆有 a i a_i ai 块石头。

一共有 t t t 组数据。

每次询问你需要输出 ?这个符号,然后输出 k k k p 1 , p 2 … p k p_1,p_2\dots p_k p1,p2pk(用空格隔开),表示询问 p 1 , p 2 … p k p_1,p_2\dots p_k p1,p2pk k k k 堆石头的总重量,然后你需要输入一个数 x x x 表示你刚刚询问得到的答案。

如果你推理出来答案,输出 !这个符号以及你得到的答案 m m m

如果你的询问次数大于 30 30 30 次,或有非法的询问,将会得到 Wrong Answer评测结果。

记得每次输出后,都要输出一行代码来刷新缓冲区,否则会得到 Idleness limit exceeded评测结果或其他评测结果。

  • 对于 C++,输出 fflush(stdout)cout.flush()
  • 对于 Java,输出 System.out.flush()
  • 对于 Pascal,输出 flush(output)
  • 对于 Python,输出 stdout.flush()

Soluton

我们可以考虑运用二分的思想,每次选目标区间的前二分之一去询问,最多需要 log ⁡ 2 n \log_2 n log2n 次,在给定范围内一定小于 30 30 30

判断方法,若当前得到了第 l l l r r r 堆的总重量,如果得到的答案等于 s u m r − s u m l − 1 sum_r-sum_{l-1} sumrsuml1,则目标堆不在这一区间内,否则反之,其中 s u m i = a 1 + a 2 + ⋯ + a i sum_i=a_1+a_2+\dots+a_i sumi=a1+a2++ai

具体步骤见代码。

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
ll sum[200200];
ll read(){
    ll x=0,f=1;
    char ch;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(){
	int n=read();
	sum[0]=0;
	for(int i=1;i<=n;i++){
		int x=read();
		sum[i]=0;
		sum[i]=sum[i-1]+x; //预处理求前缀和
	}
	int l=1,r=n;
	while(l<r){ //还没确定到具体堆的编号
		int mid=l+r>>1;
		cout<<"? "<<mid-l+1;
		for(int i=l;i<=mid;i++){ //取前二分之一
			cout<<" "<<i;
		}
		cout<<endl;
		fflush(stdout);
		int get=read();
		if(sum[mid]-sum[l-1]!=get){ //如果匹配不上说明目标堆在前半部分
			r=mid;
		}else l=mid+1; //否则在后半部分
	}
	cout<<"! "<<r<<endl;
	fflush(stdout); //记得刷新缓冲区
}
int main(){
	t=read();
	while(t--){
		solve();
	}
	return 0;
}

CF1807F Bouncy Ball

题目链接

Description

在一个 n × m n\times m n×m 的房间内有一个球,初始位置为 ( i 1 , j 1 ) (i_1,j_1) (i1,j1),目标位置为 ( i 2 , j 2 ) (i_2,j_2) (i2,j2),初始方向为字符串 d d d

  • d = UL d=\texttt{UL} d=UL,向左上移动
  • d = DR d=\texttt{DR} d=DR,向右下移动
  • d = DL d=\texttt{DL} d=DL,向左下移动
  • d = UR d=\texttt{UR} d=UR,向右上移动

当小球碰到墙壁后会反弹,新的方向与旧方向以垂直与墙壁的一条线为轴对称。(见图)

当小球移到角落时会原地返回。

若小球会移到目标位置,问最少会反弹几次(碰到角落为一次),若无法移到,则输出 -1

Solution

由于一共有 n × m n\times m n×m 个点,有四种移动方向,所以在移动最多 4 × n × m 4\times n\times m 4×n×m 步后,每个点都会移到至少一遍。

然后模拟即可。

具体步骤见代码。

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
ll read(){
    ll x=0,f=1;
    char ch;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(){
	int n=read(),m=read(),sx=read(),sy=read(),ex=read(),ey=read();
	int dx,dy,x=sx,y=sy,cnt=0;
	string s;
	cin>>s;
	if(s[0]=='D') dx=1;
	else dx=-1;
	if(s[1]=='L') dy=-1;
	else dy=1;
	ll lim=n*m*4;
	while(lim--){
		if(x==ex&&y==ey){ //移到目标点
			cout<<cnt<<endl;
			return ;
		}
		bool is=0;
		if(dx==1&&x==n){ //碰到下侧墙壁
			dx=-1;
			is=1;
		}
		if(dx==-1&&x==1){ //碰到上侧墙壁
			dx=1;
			is=1;
		}
		if(dy==1&&y==m){ //碰到右侧墙壁
			dy=-1;
			is=1;
		}
		if(dy==-1&&y==1){ //碰到左侧墙壁
			dy=1;
			is=1;
		}
		//碰到角落时,因为碰到两面墙壁,x和y都会取反
		if(is) cnt++; //有反弹
		x+=dx,y+=dy; //移动
	}
	cout<<-1<<endl;
}
int main(){
	t=read();
	while(t--){
		solve();
	}
	return 0;
}

扩展:CF724C Ray Tracing(该题也是类似于小球反弹,但做法完全不一样,我就是赛时被这题带偏的,此题难度较大,需要数论基础,有能力者可以做扩展)。

CF1807G1&G2 Subsequence Addition

easy version
hard version

Description

数列 a a a 最开始只有一个数 1 1 1,你可以进行若干次操作,每次操作你可以选取 k k k 个数( k k k 无限制,小于等于 a a a 的大小即可),将这 k k k 个数的和放入 a a a 的任意一个位置。

给定一个长度为 n n n 的序列 c c c,问 a a a 能否在进行若干次操作后转为 c c c

t t t 组数据。

Solution

若当前进行了一次操作,则新加入的数大于当前最小的数( k = 1 k=1 k=1),小于当前所有数的总和( k = n k=n k=n),所以我们对目标数列从小到大排序,若当前的数小于前面所有数的总和,说明这个数是可以实现的否则就不可能实现。

注意:目标序列至少要有一个 1 1 1

Code

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t;
int a[5050];
ll read(){
    ll x=0,f=1;
    char ch;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';
        ch=getchar();
    }
    return x*f;
}
void solve(){
	int n=read();
	ll sum=0;
	for(int i=1;i<=n;i++){
		a[i]=read();
	}
	sort(a+1,a+1+n);
	if(a[1]!=1){ //如果最小的不是1,说明该序列没有1(不可能有非正数)
		cout<<"NO"<<endl;
		return;
	}
	sum=1;
	for(int i=2;i<=n;i++){
		if(a[i]>sum){ //如果大于前缀和,则不可能实现
			cout<<"NO"<<endl;
			return ;
		}
		sum+=a[i];
	}
	cout<<"YES"<<endl;
}
int main(){
	t=read();
	while(t--){
		solve();
	}
	return 0;
}
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值