ZAFUACM - 7.9个人赛补题 A - C E G H

A CodeForces - 495B

题目链接

题意

给出一个 a 一个 b ,求出有多少不同的 x 满足 a mod x = b,输出 x 个数,如果有无数个就输出 “infinity”

思路

  • 当 a = b 时,有无穷多个可以取的 x
  • 当 a < b 时,x 取任何数都不成立
  • 当 a > b 时,就是求 a - b 有多少个大于 b 的因子

为了防止TLE,求因子个数时,循环结束条件应该写 sqrt(a - b) (痛

代码

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

typedef long long LL;

int a, b;

int main()
{
    cin >> a >> b;
    if (a == b) cout << "infinity";
    else if (a < b) cout << 0;
    else
    {
        int diff = a - b;
        int i = 1;
        int ans = 0;
        while (diff / i >= sqrt(diff))
        {
            if (diff % i == 0) // i 是 diff 的一个因子
            {
                if (diff / i > b) ans ++ ; // 加的是因子 diff / i
                if (i > b)
                {
                    ans ++ ; // 加的是因子 i
                    if (diff == i * i) ans -- ; // 当 i == diff / i 删去重复加的
                }
            }
            i ++ ;
        }
        cout << ans;
    }
    return 0;
}

B CodeForces - 495A

题目链接

题意

火柴棒摆数字,给出两个数字,给出的数字可能是某个数字缺少火柴棒后的结果,输出这两个数字有多少种可能的情况

思路

  • 给出的数字是 8 时分别有 1 种情况
  • 给出的数字是 0 / 2 / 6 / 9 时分别有 2 种情况
  • 给出的数字是 3 / 4 时分别有 3 种情况
  • 给出的数字是 5 时分别有 4 种情况
  • 给出的数字是 7 时分别有 5 种情况
  • 给出的数字是 1 时分别有 7 种情况

代码

#include <iostream>
#include <algorithm>

using namespace std;

char s[2];

int main()
{
    cin >> s;
    int a = s[0] - '0';
    int b = s[1] - '0';
    
    if (a == 0 || a == 2 || a == 6 || a == 9) a = 2;
    else if (a == 1) a = 7;
    else if (a == 3 || a == 4) a = 3;
    else if (a == 5) a = 4;
    else if (a == 7) a = 5;
    else if (a == 8) a = 1;
    
    if (b == 0 || b == 2 || b == 6 || b == 9) b = 2;
    else if (b == 1) b = 7;
    else if (b == 3 || b == 4) b = 3;
    else if (b == 5) b = 4;
    else if (b == 7) b = 5;
    else if (b == 8) b = 1;
    
    cout << a * b;
    return 0;
}

C CodeForces - 495C

题目链接

题意

给出一个字符串,包含三个字符 ‘(’ ‘)’ ‘#’
每一个 ‘#’ 都可以替换成 >= 1 个 ‘)’
对于任意一个 i 都满足以下条件:

  • 前 i 个字符中,‘(’ 的数量大于等于 ‘)’ 的数量
  • 整个字符串中,‘(’ 和 ‘)’ 的数量相等

求每个 ‘#’ 要替换成多少个 ‘)’,输出任意一种情况

思路

这一题本人wa了三次才过,debug到想哭 TAT

首先遍历,求出每个字符的个数
如果 ‘(’ 的个数比 ‘)’ 少或二者相等,直接输出 -1,因为 ‘#’ 还会变成 ‘)’,不可能保证两者相等
否则,判断 ‘(’ 与 ‘)’ 数量的差值,如果差值小于 ‘#’ 的个数,直接输出 -1,理由同上

我们假设除了最后一个 ‘#’ 之外的所有 ‘#’ 都只替换成一个 ‘)’,少的 ‘)’ 全部由最后一个 ‘#’ 补全
那么重新遍历替换后的字符串,看看有没有不满足条件的地方,有就输出 -1,没有就按照上面的思路输出

说一下我自己debug过程中发现的容易漏掉的点:

  • 在第一遍还没有替换的遍历时就要判断是不是满足条件
    否则会过不了类似于 “( ) ) ( ( #”
  • 如果 ‘(’ ‘)’ 的差值与 ‘#’ 的个数相等,也要重新遍历判断在每一个位置上是否满足条件
    否则会过不了类似于 “( # ) ( ( )”

代码

( 比赛赶时间想到哪写哪了所以写的可能会比较乱,欢迎大佬提供更简洁的思路~

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

string s;

int main()
{
    cin >> s;
    int suma = 0, sumb = 0, sumc = 0, diff;
    // 第一轮遍历 得出三种字符的个数
    for (int i = 0; i < s.size(); i ++ )
    {
        if (s[i] == '(') suma ++ ;
        if (s[i] == ')') sumb ++ ;
        if (s[i] == '#') sumc ++ ;
        if (suma < sumb) // 判断当前位置是否满足条件
        {
            cout << -1;
            return 0;
        }
    }
    if (suma <= sumb) cout << "-1"; // 判断整个字符串是否满足条件
    else
    {
        diff = suma - sumb;
        if (diff < sumc) // 判断条件
        {
            cout << -1;
            return 0;
        }
        else if (diff == sumc) // 这种情况所有的'#'都替换成一个')'
        {
            int a = 0, b = 0;
            for (int i = 0; i < s.size(); i ++ )
            {
                if (s[i] == '(') a ++ ;
                else if (s[i] == ')' || s[i] == '#') b ++ ;				if (a < b) // 判断当前位置是否满足条件
                {
                    cout << -1;
                    return 0;
                }
            }
            for (int i = 0; i < sumc; i ++ ) cout << 1 << endl;
        }
        else
        {
            int sa = 0, sb = 0, sc = 0;
            // 遍历替换后的字符串
            for (int i = 0; i < s.size(); i ++ )
            {
                if (s[i] == '(') sa ++ ;
                if (s[i] == ')') sb ++ ;
                if (s[i] == '#')
                {
                    sc ++ ;
                    if (sc == sumc) sb += diff - sumc + 1;
                    else sb ++ ;
                }
                if (sa < sb) // 判断当前位置是否满足条件
                {
                    cout << -1;
                    return 0;
                }
            }
            for (int i = 0; i < sumc - 1; i ++ )
            	cout << 1 << endl;
            cout << diff - sumc + 1;
        }
    }
    return 0;
}

D CodeForces - 495D

题目链接
(复习完KMP和dp再来补)

E CodeForces - 198B

题目链接

题意

两面竖着的墙,墙上不同区域有的能走有的不能走,每次可以在同一面墙上向上 1m 或者向下 1m 或者跳到另一面墙上高度升高给定的值,另外还有不断上升的水位,人每跳一次水上升 1m,给出墙的高度问能不能跳出去

思路

深搜,每次只要满足:

  1. 当前位置能走
  2. 当前位置没走过
  3. 水没淹过当前位置

就可以继续搜
注意!!!这个地方调到崩溃:深搜时顺序是先看向下 1m 的再看其他两个向上的(因为考虑到水位上涨,先要尽可能往下搜)

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n, k, water;
char wall[2][N];
bool visit[2][N];

bool dfs(int pos, int x)
{
    if (x > n) return true;
    if (wall[pos][x] == 'X' || x < water || visit[pos][x]) return false;
    visit[pos][x] = true;
    water ++ ;
    bool capable = (dfs(pos, x - 1) || dfs(1 - pos, x + k) || dfs(pos, x + 1));
    water -- ;
    return capable;
}

int main()
{
    cin >> n >> k;
    water = 1;
    for (int i = 1; i <= n; i ++ ) cin >> wall[0][i];
    for (int i = 1; i <= n; i ++ ) cin >> wall[1][i];

    if (dfs(0, 1)) cout << "YES";
    else cout << "NO";
    return 0;
}

F CodeForces - 466E

题目链接

题意

三个操作:

  1. 输入 x y,y 是 x 的 boss
  2. 输入 x,第 i 个文件从 x 手上开始传
  3. 输入 x y,询问文件 y 能不能传到 x 手上

思路

赛时想到了并查集,可惜代码实现能力太菜也没有并查集的板子导致寄了
补题时想到是不是能用遍历做,y 是 x 的 boss 就相当于建立一条 x -> y 的边,询问时看看能不能从文件 y 的第一个持有人走到 x 即可,然后用 bfs 写了一下,TLE了
还是得用并查集进行路径压缩,但是有个问题:并非 x y 的祖先结点一样就能从 x 走到 y,因为可能出现这样的情况:
在这里插入图片描述
这个问题还没有想到解决方法,希望之后能补上

G CodeForces - 546D

题目链接

题意

给出 a b 两个数,求 a! / b! 每次除以任意一个数最多可以除多少次

思路

( 赛时刚看到题的内心 be like:寄 先预定一个TLE
之后果然成功TLE了~
嗯……这题主要还是预先打表(不要暴力打表) + 前缀和
题目是要求 (b + 1) * (b + 2) * … * a 的质因子个数,那就求出每个数的质因子,再整个前缀和数组,然后用 s[a] - s[b] 就快乐AC啦~(实际上既不快乐也没有AC / QAQ)

(ps.还会卡cin…

代码

#include <iostream>
#include <algorithm>
 
const int N = 5000010;
 
using namespace std;
 
int yz[N]; // i的质因子个数
int a, b;
 
int main()
{	
	int t;
	cin >> t;
	
	// 打表
    for (int i = 2; i < N; i ++ )    
        if (yz[i] == 0)    
            for (int j = i; j < N; j += i )    
                yz[j] = yz[j / i] + 1;
	// 生成前缀和数组			    
    for (int i = 1; i < N; i ++ ) yz[i] += yz[i - 1]; 
		
	while (t -- )
	{
		int ans = 0;
		scanf("%d %d", &a, &b);
		printf("%d\n", yz[a] - yz[b]);//利用了a!/b!的特性
	}
} 

H CodeForces - 849C

题目链接

题意

序列中每两个部分合并会消耗相同字母在两个部分中数量的乘积之和
比如合并 ‘a’ ‘a’ ,在第一个部分中 a 出现了 1 次,第二个部分中 a 也出现了一次,没有其他字母出现,所以这一步就消耗 1 * 1 = 1
再比如合并 ‘a’ ‘b’ ,在第一个部分 a 出现了 1 次,第二个部分没有出现 a,第一部分 b 出现了 0 次,第二部分出现了 1 次,所以这一步消耗 1 * 0 + 0 * 1 = 0
给定一个值,要求给出一个序列,起初序列中的每个字母单独为一个部分,从开始到形成连续序列消耗的数量为给定值

思路

我首先是考虑到,需要自己输出的题目不会出现特别复杂的字母组合,所以先枚举一下:

  • ‘a’ ‘a’ 合并消耗 1
  • ‘a’ ‘a’ ‘a’ 合并消耗 3 (= 1 + 2) (先合并 ‘a’ + ‘a’ = 1,再合并 ‘a a’ + ‘a’ = 2)
  • ‘a’ ‘a’ ‘a’ ‘a’ 合并消耗 6 (= 1 + 2 + 3)

所以总结出规律,n 个 a 合并需要消耗 1 + 2 + … + (n - 1),也就是 (1 + (n - 1)) * (n - 1) / 2,化简后为 n * (n - 1) / 2
每当添加进不同字母,只会在原来的基础上加上添加进来的字符串所消耗的数量(动手试试就知道)
所以我们可以按照字母从小到大开始输出字符串,也就是先只使用 a,构造出小于等于给定值的最大值,再使用 b,……依此类推

注意要特判 n == 0 !!!

代码

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

int n;

int main()
{
    cin >> n;
    if (n == 0)
    {
        cout << 'a';
        return 0;
    }
    int x = sqrt(n * 2);
    int diff = n - (x * (x - 1) / 2);
    int k = 1; // k 表示目前添加到哪一个字母了
    for (int i = 0; i < x; i ++ ) cout << 'a';
    while (diff != 0) // diff 等于0就表示构造完成
    {
        if (diff == 1)
        {
            printf("%c%c", 'a' + k, 'a' + k);
            return 0;
        }
        x = sqrt(diff * 2);
        diff = diff - (x * (x - 1) / 2);
        for (int i = 0; i < x; i ++ ) printf("%c", 'a' + k);
        k ++ ;
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Texcavator

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值