AcWing Round #21

A. 整数幂

题目链接


给定两个整数 k k k l l l,请判断是否存在一个正整数 n n n,满足 k n = l kn=l kn=l

输入格式

第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据占两行,第一行包含整数 k k k,第二行包含整数 l l l

输出格式

每组数据输出一行结果,如果存在 n n n,则输出 YES,否则输出 NO

数据范围

前三个测试点满足, 2 ≤ k , l ≤ 100 2\leq k,l\leq 100 2k,l100
所有测试点满足, 1 ≤ T ≤ 10 1\leq T\leq 10 1T10 2 ≤ k , l ≤ 2 31 − 1 2\leq k,l\leq 2^{31}−1 2k,l2311

输入样例:
2
5
25
3
8
输出样例:
YES
NO
题目分析:

手速题,暴力枚举。

Code

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

using namespace std;

typedef long long LL;

LL get_c(int a, int k)
{
    LL res = 1;
    for (int i = 0; i < k; i ++ )
        res *= a;
    return res;
}

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        int k, l;
        cin >> k >> l;
        bool flag = false;
        for (int i = 0; i <= 50; i ++ )
            if (get_c(k, i) == l) 
            {
                flag = true;
                break;
            }
        if (flag) puts("YES");
        else puts("NO");
    }
    return 0;
}

B. 变成1

题目链接


给定一个二进制数 x x x,在它变为 1 1 1 之前,不断对它进行如下操作:

  • 如果 x x x 为奇数,则将 x x x 1 1 1

  • 如果 x x x 为偶数,则将 x 除以 2 2 2

请问,多少次操作后, x x x 会变为 1 1 1

输入格式

共一行,一个 01 字符串,表示二进制数 x x x

输出格式

一个整数,表示所需操作次数。

数据范围

前六个测试点满足, x x x 的位数不超过 11 11 11
所有测试点满足, x x x 的首位不为 0 0 0,且位数不超过 1 0 6 10^6 106

输入样例1:
1
输出样例1:
0
输入样例2:
1001001
输出样例2:
12
输入样例3:
101110
输出样例3:
8

题目分析:

由于 x x x 最多可以是 1 0 6 10^6 106 位,因此需要使用高精度根据题意进行模拟。

但实际上可以不用高精度的方式,转而对二进制数串进行操作,利用双指针的思想处理。

例如对于一个给定的二进制数串 101110,从后往前操作:
在这里插入图片描述
手动模拟一下可以发现规律:若当前这一位是 0 0 0 时,直接操作数 + 1,然后右移一位;若当前这一位是 1 1 1,需要加 1 1 1,那就需要找到所有连续的 1 1 1 的个数,因为加上 1 1 1 之后这些连续的 1 1 1 都会由于进位的原因全部变成 0 0 0,那么操作数就等于连续 1 1 1 的个数加上 1 1 1

这样,我们就可以设置两个指针 i i i j j j,然后累加操作步数即可,具体步骤见代码。

Code

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

using namespace std;

int main()
{
    string str;
    cin >> str;
    reverse(str.begin(), str.end()); // 将 01 串倒过来操作
    int cnt = 0;
      
    for (int i = 0; i < str.size() - 1; i ++ )
    {
        if (str[i] == '0') ++ cnt; 
        else
        {
            int j = i;
            while (j < str.size() && str[j] == '1') j ++ ;
            cnt += j - i + 1;
            str[j] = '1';
            i = j - 1;
        }
    }
    cout << cnt << endl;
    return 0;
}

C. 最大公约数

题目链接


给定两个正整数 a , m a,m a,m,其中 a < m a<m a<m

请你计算,有多少个小于 m m m 的非负整数 x x x 满足:

gcd ⁡ ( a , m ) = gcd ⁡ ( a + x , m ) \gcd(a,m) = \gcd(a+x,m) gcd(a,m)=gcd(a+x,m)

输入格式

第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据占一行,包含两个整数 a , m a,m a,m

输出格式

每组数据输出一行结果,一个整数,表示满足条件的非负整数 x x x 的个数。

数据范围

前三个测试点满足, 1 ≤ T ≤ 10 1\leq T\leq 10 1T10
所有测试点满足, 1 ≤ T ≤ 50 1\leq T\leq 50 1T50 1 ≤ a < m ≤ 1 0 10 1\leq a<m\leq 10^{10} 1a<m1010

输入样例:
3
4 9
5 10
42 9999999967
输出样例:
6
1
9999999966
题目分析:

a < m a < m a<m。令 d = gcd ⁡ ( a , m ) = gcd ⁡ ( a + x , m ) d = \gcd(a, m) = \gcd(a + x, m) d=gcd(a,m)=gcd(a+x,m),则 d ∣ a ,   d ∣ m d\mid a,\ d\mid m da, dm,而又 ∵ \because d ∣ a ,   d ∣ m d\mid a,\ d\mid m da, dm,且 d ∣ ( a + x ) d\mid (a + x) d(a+x) ∴ d ∣ x \therefore d\mid x dx

a ′ = a d ,   m ′ = m d ,   x ′ = x d a' = \dfrac{a}{d},\ m' = \dfrac{m}{d},\ x' = \dfrac{x}{d} a=da, m=dm, x=dx,则 gcd ⁡ ( a ′ + x ′ , m ′ ) = 1 \gcd(a' + x', m') = 1 gcd(a+x,m)=1,问题转化为:有多少个满足要求的 x ′ x' x 使得 ( a ′ + x ′ ) (a' + x') (a+x) m ′ m' m 互质。

∵ 0 ≤ x < m \because 0\leq x < m 0x<m ∴ 0 ≤ x ′ < m \therefore 0\leq x' < m 0x<m,两边同加上 a ′ a' a,得 a ′ ≤ x ′ < a ′ + m ′ a'\leq x' < a' + m' ax<a+m,即求 [ a ′ ,   a ′ + m ′ ) [a',\ a' + m') [a, a+m) 中共有多少个数与 m ′ m' m 互质。

在这里插入图片描述

图中红色的两段 [ 0 , a ′ ) [0, a') [0,a) [ m ′ , a ′ + m ′ ) [m', a' + m') [m,a+m)   m o d     m ′ \bmod\ m' mod m 的意义下数值是一样的,那么与之互质的数的情况也就是一样的,故我们求 [ a ′ , a ′ + m ′ ) [a', a' + m') [a,a+m) 中有多少个与 m ′ m' m 互质的数,就可以转化为求 [ 0 , m ′ ) [0, m') [0,m) 中与 m ′ m' m 互质的数的个数,求一下 m ′ m' m 的欧拉函数 φ ( m ′ ) \varphi (m') φ(m) 即可。

关于欧拉函数的求解和证明过程,可以参考:AcWing 873. 欧拉函数

注意,本题不可使用 筛法求欧拉函数 进行求解,筛法求欧拉函数时间复杂度是线性的,可以求出 1 ∼ n 1\sim n 1n 中每个数的欧拉函数,但本题的部分数据可能达到 1 0 10 10^{10} 1010,线性筛不能够在时限内求出所需要数的欧拉函数

时间复杂度分析:

每组数据只需要求出其欧拉函数即可,复杂度为 n \sqrt n n ,一共有 50 50 50 组数据,值最大可达 1 0 10 10^{10} 1010,因此总的计算量约为 50 × 1 0 10 ≈ 5 × 1 0 6 50\times \sqrt {10^{10}} \approx 5\times 10^6 50×1010 5×106

Code

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

using namespace std;

typedef long long LL;

LL gcd(LL a, LL b)
{
    return b ? gcd(b, a % b) : a;
}

LL phi(LL m)
{
    LL res = m;
    for (int i = 2; i <= m / i; i ++ )
        if (m % i == 0)
        {
            res = res / i * (i - 1);
            while (m % i == 0) m /= i;
        }
    if (m > 1) res = res / m * (m - 1);
    return res;
}

int main()
{
    int T;
    cin >> T;
    while (T -- )
    {
        LL a, m;
        cin >> a >> m;
        m = m / gcd(a, m);
        cout << phi(m) << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值