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 @ 2024-12-08 10:01  才瓯  阅读(64)  评论(0)    收藏  举报