为了能到远方,脚下的每一步都不能少。|

larryyu_blog

园龄:2年5个月粉丝:5关注:17

2023-03-25 14:45阅读: 147评论: 0推荐: 0

Codeforces Round 859 (Div. 4) 题解集


比赛链接

CF1807A Plus or Minus

题目链接

Description

给定 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 包糖果,每包有 ai 糖果,如果该包糖果数是偶数,Mihai会拿走,否则 Bianca会拿走。

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

Solution

我们可以将所有偶数排在数列前面,这样开始 kk 为偶数的个数)包,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 的字符串 s,你可以将每种字母转为 01,将所有字母转换完后,问能否得到一串由 01 交替而成的字符串(即相邻两个字符不同)。

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 的数列 a,有 q 次询问,每次给定 l,r,k,问将 al,al+1ar 全都转为 k 后,该数列的总和是否为奇数。

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

Solution

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

操作部分总和为 (rl+1)×k,未操作部分为 sumn(sumrsuml1),其中sumi 表示前 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

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

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

首先输入 n,接下来输入 n 个数 a1,a2an,其中 ai 表示第 i 堆有 ai 块石头。

一共有 t 组数据。

每次询问你需要输出 ?这个符号,然后输出 kp1,p2pk(用空格隔开),表示询问 p1,p2pkk 堆石头的总重量,然后你需要输入一个数 x 表示你刚刚询问得到的答案。

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

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

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

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

Soluton

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

判断方法,若当前得到了第 lr 堆的总重量,如果得到的答案等于 sumrsuml1,则目标堆不在这一区间内,否则反之,其中 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 的房间内有一个球,初始位置为 (i1,j1),目标位置为 (i2,j2),初始方向为字符串 d

  • d=UL,向左上移动
  • d=DR,向右下移动
  • d=DL,向左下移动
  • d=UR,向右上移动

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

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

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

Solution

由于一共有 n×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 最开始只有一个数 1,你可以进行若干次操作,每次操作你可以选取 k 个数(k 无限制,小于等于 a 的大小即可),将这 k 个数的和放入 a 的任意一个位置。

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

t 组数据。

Solution

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

注意:目标序列至少要有一个 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;
}

本文作者:larryyu_blog

本文链接:https://www.cnblogs.com/larryyu/p/17254735.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   larryyu_blog  阅读(147)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起