CF补题 964-Div.4

CF补题 964-Div.4-20241206

Dashboard - Codeforces Round 964 (Div. 4) - Codeforces

A:

题目大意:给定一个两位数正整数 n ,求其位数之和

#include <stdio.h>
int main()
{
int n;
scanf("%d",&n);
while(n--){
int x,sum=0;
scanf("%d",&x);
while(x!=0){
sum+=x%10;
x/=10;
}
printf("%d\n",sum);
}
return 0;
}

签到,简单的取各个位数然后计算

B:

题目大意:两人各有两张牌,每轮两人任意翻开其中一张,点数大的获胜,求第一个人能胜利的场数

#include <stdio.h>
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int a1,a2, b1,b2;
int win = 0;
scanf("%d %d %d %d", &a1, &a2, &b1, &b2);
if ((a1 > b1 && a2 >= b2) || (a1 >= b1 && a2 > b2)) win ++;
if ((a1 > b2 && a2 >= b1) || (a1 >= b2 && a2 > b1)) win ++;
printf("%d\n", win*2);
}
return 0;
}

每次随机抽取两张中的一张,所以我们可以依次判断能获胜的情况

(a1 > b1 && a2 >= b2) || (a1 >= b1 && a2 > b2)

同位比较,能获胜的情况是:

选出的 a1 一定比 b1 大,那么保证能赢则需要 a2 不小于 b2

反之,选出的 a1 不小于 b1 ,那么保证能赢则需要 a2 一定大于 b2

(a1 > b2 && a2 >= b1) || (a1 >= b2 && a2 > b1)

错位比较,能获胜的情况是:

选出的 a1 一定比 b2 大,那么保证能赢则需要 a2 不小于 b1

反之,选出的 a1 不小于 b2 ,那么保证能赢则需要 a2 一定大于 b1

C:

题目大意:给出若干个区间,判断区间的间隙中是否存在解

#include <stdio.h>
int l[200010], r[200010];
int main()
{
int t;
scanf("%d", &t);
while (t--)
{
int n, s, m;//n个区间
scanf("%d %d %d", &n, &s, &m);
for (int i = 0; i < n; i++)
scanf("%d %d", &l[i], &r[i]);//读入区间
int temp = 0, flag = 0;//定义变量,和状态标志
for (int i = 0; i < n; i++) {
if (l[i] - temp >= s) {//当前的左区间减去上一个的右区间,为一个间隔
flag = 1;//若间隔大于我们要求的解
printf("YES\n");//能够满足题意输出yes
break;
}
if (i == n - 1) {//对最后一个区间进行特判,
if (m - r[i] >= s) {//如果能有解,则输出yes
flag = 1;
printf("YES\n");
break;
}
}
temp = r[i];//将这次的右区间存到temp中,进入下一轮循环,计算间隔
}
if (flag == 0) printf("NO\n");//如果遍历完仍然无解,则输出no
}
return 0;
}

注意,需要将数据全部存进才能再进行判断

D:

题目大意:给出一个字符串(存在通配符?)和子串,判断子串是否在字符串的子序列中

#include <stdio.h>
#include <string>//引用头文件
#include <iostream>
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
string s, t;
cin >> s >> t;//读入string
int i = 0, j = 0;//双指针
while (i < s.length() && j < t.length()) {
//遍历两个字符串,遍历完s,或查找到了子序列就退出
if (s[i] == t[j]) i++, j++;//如果当前的字符相等,则把两个指针都往后移动
else if (s[i] == '?') s[i] = t[j], i++, j++;//如果当前s的字符为?,就赋值
else i++;//否则,移动i
}
for (int k = 0; k < s.length(); k++) {
if (s[k] == '?') s[k] = 'a';
}//完善s序列
if (j == t.length())//如果j指向了t的最后元素,说明存在子序列
cout << "YES" << endl << s << endl;//输出yes和s
else
cout << "NO" << endl;
}
return 0;
}

使用双指针维护两个字符串

这里的一个关键点:i 移动时,如果当前指向的元素之后能构成一个解,则这个元素之前的和它相同的元素也能构成一个解,例:

a b c c a g h l e

如果需要查找 ale 这个子序列,我们的 is[0] 开始,s[0]==t[0] ,但是下一位不相同,所以移动 i 往后查找一个元素使 s[i]==t[1] ,因为我们这时已经找到了一个元素和 t[0] 相同,在之后的过程中,只需要找剩余的元素即可

E:

题目大意:给出一个整数区间,我们可以对区间的任意两个数同时进行 *3/3 操作,求使区间所有数变为 \(0\) 的最少操作数

#include <stdio.h>
int a[200010],sum[200010];
void pre(){//预处理
for(int i=1;i <=200010;i++){
a[i]=a[i/3]+1;//递推
sum[i]=sum[i-1]+a[i];//前缀和
}
}
int main()
{
pre();
int T;
scanf("%d", &T);
while (T--)
{
int l, r;
scanf("%d %d", &l, &r);
printf("%d\n",sum[r]-sum[l-1]+a[l]);//计算总操作数
}
return 0;
}

采用暴力会严重超时,可以想到使用前缀和+预处理进行优化

由于我们的区间是 l~r ,其中的任意数都可以转化为 \(k+0,k+1,k+2,(k+1)+1,·····\)\(k\) 为整除以 \(3\) 的商)

我们的 l 必然是 \(k+0,k+1,k+2\) 其中的一项,所以 l 通过 /3 的操作,取到 \(0\) 时,一定是操作最少的元素之一

而后,区间内的其他元素,通过已经为 \(0\) 的元素,再取到 \(0\) 会十分容易

由于我们对 l 进行 /3 时,某个其他元素会进行 *3 操作,故我们的总操作数为 所有元素/3的操作数+l/3->0的操作数

可以对所有区间内的数进行预处理,最后通过前缀和 \(O(1)\) 输出

F:逆元+组合数学,下次再学

G1:

题目大意:在数轴上缺失了一个数,这个数之前的数保持原样,这个数之后的数 \(+1\) ,通过至多 \(10\) 次询问,找到这个数

#include <iostream>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--){
int l=1,r=999,ans,flag=0;
while (l<r){
int mid=l+r>>1;
cout<<"? 1 "<<mid<<endl;
cin>>ans;
if (ans>=2*mid){
cout<<"! 1"<<endl;
flag=1;
break;
}
if (ans==mid) l=mid+1;
else r=mid;
}
if (!flag) cout<<"! "<<l<<endl;
}
return 0;
}

一道互动题,使用 coutendl 自动刷新缓冲区(不用 fflush
把矩形的面积转化为线的长度再二分查找(\(log_2{999}<10\)

特别的,如果这个数为 \(1\) 时,我们查询的数一定会大于等于 mid 的两倍

正常:1 2 3 4 5 6 //l = 1 , mid = 3
缺失:2 3 4 5 6 7 //l` = 2 , mid = 3
posted @   才瓯  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示