目录
- CF1807A Plus or Minus
- CF1807B Grab the Candies
- CF1807C Find and Replace
- CF1807D Odd Queries
- CF1807E Interview
- CF1807F Bouncy Ball
- CF1807G1&G2 Subsequence Addition
比赛链接
CF1807A Plus or Minus
Description
给定 \(a,b,c\) 三个整数。
- 若 \(a+b=c\),输出
+
。 - 若 \(a-b=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\) 包糖果,每包有 \(a_i\) 糖果,如果该包糖果数是偶数,Mihai
会拿走,否则 Bianca
会拿走。
两人会按照顺序依次拿走 \(a_1,a_2\dots a_n\),现在可以对数列重新排序,问是否存在一种情况,使得任意时刻 Mihai
拿到的糖果数严格大于 Bianca
拿到的。
Solution
我们可以将所有偶数排在数列前面,这样开始 \(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\) 的字符串 \(s\),你可以将每种字母转为 \(0\) 或 \(1\),将所有字母转换完后,问能否得到一串由 \(0\) 和 \(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\) 的数列 \(a\),有 \(q\) 次询问,每次给定 \(l,r,k\),问将 \(a_l,a_{l+1}\dots a_r\) 全都转为 \(k\) 后,该数列的总和是否为奇数。
注意,每次操作对后续操作没有影响,即每次操作都是在原数列 \(a\) 上做操作。
Solution
我们将每次操作完后分为两部分:操作部分和未操作部分。
操作部分总和为 \((r-l+1)\times k\),未操作部分为 \(sum_n-(sum_r-sum_{l-1})\),其中\(sum_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-1\) 堆都只有重量为一克的石头,剩下一堆有则有一块有两克的石头和若干重量为一克的石头。
你的任务是在 \(30\) 次询问内推理出那一堆有重量为两克的石头是第几堆。
首先输入 \(n\),接下来输入 \(n\) 个数 \(a_1,a_2\dots a_n\),其中 \(a_i\) 表示第 \(i\) 堆有 \(a_i\) 块石头。
一共有 \(t\) 组数据。
每次询问你需要输出 ?
这个符号,然后输出 $k $ 及 \(p_1,p_2\dots p_k\)(用空格隔开),表示询问 \(p_1,p_2\dots p_k\) 这 \(k\) 堆石头的总重量,然后你需要输入一个数 \(x\) 表示你刚刚询问得到的答案。
如果你推理出来答案,输出 !
这个符号以及你得到的答案 \(m\)。
如果你的询问次数大于 \(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\) 次,在给定范围内一定小于 \(30\)。
判断方法,若当前得到了第 \(l\) 到 \(r\) 堆的总重量,如果得到的答案等于 \(sum_r-sum_{l-1}\),则目标堆不在这一区间内,否则反之,其中 \(sum_i=a_1+a_2+\dots+a_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();
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\times m\) 的房间内有一个球,初始位置为 \((i_1,j_1)\),目标位置为 \((i_2,j_2)\),初始方向为字符串 \(d\)。
- \(d=\texttt{UL}\),向左上移动
- \(d=\texttt{DR}\),向右下移动
- \(d=\texttt{DL}\),向左下移动
- \(d=\texttt{UR}\),向右上移动
当小球碰到墙壁后会反弹,新的方向与旧方向以垂直与墙壁的一条线为轴对称。(见图)
当小球移到角落时会原地返回。
若小球会移到目标位置,问最少会反弹几次(碰到角落为一次),若无法移到,则输出 -1
。
Solution
由于一共有 \(n\times m\) 个点,有四种移动方向,所以在移动最多 \(4\times n\times 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
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;
}
标签:ch,int,题解,ll,Codeforces,long,while,Div,getchar
From: https://www.cnblogs.com/larryyu/p/17254735.html