数论(c++)

1.等差数列

ACWing1246. 等差数列
数学老师给小明出了一道等差数列求和的题目。

但是粗心的小明忘记了一部分的数列,只记得其中 N 个整数。

现在给出这 N 个整数,小明想知道包含这 N 个整数的最短的等差数列有几项?

输入格式
输入的第一行包含一个整数 N。

第二行包含 N 个整数 A1,A2,⋅⋅⋅,AN。(注意 A1∼AN 并不一定是按等差数
列中的顺序给出)

输出格式
输出一个整数表示答案。

数据范围
2≤N≤100000,
0≤Ai≤109
输入样例:

5
2 6 4 10 20

输出样例:

10

样例解释
包含 2、6、4、10、20 的最短的等差数列是 2、4、6、8、10、12、14、16、18、20。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010;
int a[N];
int gcd(int a, int b)//辗转相处
{
    return (b ? gcd(b, a%b): a);
}
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i<n; i++) scanf("%d", &a[i]);
    sort(a,a + n);//排序
    
    int d = 0;
    for(int i = 1; i < n; i++) d = gcd(d, a[i] - a[0]);//找到所有等差的最大公约数
    if(!d) printf("%d\n", n);
    else printf("%d\n", (a[n-1] - a[0])/d +1);//项数为首项+末项  除以公差 + 1
    
    return 0;
}

2.X的因子链

AcWing1295. X的因子链
输入正整数 X,求 X 的大于 1 的因子组成的满足任意前一项都能整除后一项的严格递增序列的最大长度,以及满足最大长度的序列的个数。

输入格式
输入包含多组数据,每组数据占一行,包含一个正整数表示 X。

输出格式
对于每组数据,输出序列的最大长度以及满足最大长度的序列的个数。

每个结果占一行。

数据范围
1≤X≤220
输入样例:

2
3
4
10
100

输出样例:

1 1
1 1
2 1
2 2
4 6

在这里插入图片描述
x = p1d1 X p2d2 X p3d3…pkdk
pi是质数,di > 0

d1 + d2 + …dk个质因子
最大长度: d1 + d2 …dk

映射:
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstdio>

using namespace std;

typedef long long LL;

const int N=(1<<20)+10;

LL f[20+10];    //用于存储阶乘和

int primes[N];//用于存放素数
int cnt;    //记录素数的个数 也作为primes数组的下标使用
bool st[N];//记录当前的数是否有被筛过
int minp[N];

void get_primes(int n) //选出0-n中所有的素数,线性筛法
{
    for(int i=2;i<n;i++)
    {
        if(st[i]==false)
        {
            st[i]=true;
            primes[cnt]=i;
            minp[i]=i;
            cnt++;
        }
        for(int j=0;i*primes[j]<=n;j++)
        {
            st[i*primes[j]]=true;
            minp[i*primes[j]]=primes[j];
            if(i%primes[j]==0)
                break;
        }
    }
}

int x;

int main()
{
    get_primes(N-1);    //由于本题是多个数据输入所以直接筛好所有素数

    f[0]=1;

    for(int i=1;i<=20;i++)
        f[i]=f[i-1]*i;

    while(scanf("%d",&x)!=-1)
    {
        LL sum=1;
        LL total=0; //记录X的因子数

        while (x>1)
        {   
            int p=minp[x];
            int k=0;    //记录每个因子的出现次数
            while(x%p==0)
            {
                k++;
                total++;
                x/=p;
            }
            sum*=f[k];
        }

        printf("%lld %lld\n",total,f[total]/sum);

    }
    return 0;
}

3. 聪明的燕姿

AcWing1296. 聪明的燕姿
城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。

可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!

燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于 S。

所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)。

可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。

输入格式
输入包含 k 组数据。

对于每组数据,输入包含一个号码牌 S。

输出格式
对于每组数据,输出有两行。

第一行包含一个整数 m,表示有 m 个等的人。

第二行包含相应的 m 个数,表示所有等的人的号码牌。

注意:你输出的号码牌必须按照升序排列。

数据范围
1≤k≤100,
1≤S≤2×109
输入样例:

42

输出样例:

3
20 26 41

题意: 求有多少个数的正约数之和为S
在这里插入图片描述

一下图片参考Akari
在这里插入图片描述
在这里插入图片描述

注意点

  • 1.求质数的过程应该用线性筛的方法去做
  • 2.dfs(last,product,S)其中last应该初始化为-1
  • 这样我们在一开始判断S-1大于前面的质数的时候应该特判,S-1 > ((last < 0) ? 0 : primes[last])
  • 因为最小是(1+2)那么S起码是2,所以应该有S > 1 即S-1 > 0
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 50000;
int primes[N], cnt;
int ans[N], len;
bool st[N];

bool is_prime(int x)
{
    if(x <N) return !st[x];//没有被筛过说明就是质数,返回true
    for(int i = 0; primes[i] <= x /primes[i]; i++)
    if(x % primes[i] == 0) return false;
    
    return true;
}
void get_primes(int n)
{
    for(int i = 2; i <= n; i++)
    {
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j]*i <= n; j++)
        {
            st[primes[j]*i] =true;
            if(i % primes[j] == 0) break;
        }
    }
}

void dfs(int last,int prod, int s )//last表示上一个用的质数的下标是什么,prod当前最高次项的结果,S表示每次处理后剩余多少
{
    if(s == 1)
    {
        ans[len++] = prod;
        return;
    }
     //比如20 = 2^2 * 5
    //N = P1^a1 * P2^a2 * ... * Pn^an
    //S = (1+p1+p1^2+...+p1^a1)(1+p2+p2^2+...+p2^a2)...(1+pn+pn^2+...+pn^an)
    //42 = (1 + 2 + 2^2)*(1 + 5),其中2^2和5就分别是最高次项p1^2*p2^1
    if(s-1 > ((last < 0)  ? 0: primes[last])&& is_prime(s - 1))
    {
        ans[len++] = prod*(s - 1);
        
    }
    
    for(int i = last +1; primes[i] <= s/primes[i]; i++)
     {
         int p = primes[i];
         for(int j = 1 + p, t = p; j <= s; t*=p, j +=t)//j为所有数的和,t存最大次幂
           if(s % j == 0)
            dfs(i, prod*t, s/j);
     }
}
int main()
{
    get_primes(N-1);
    int s;
    while(cin >> s)
    {
        len = 0;
        dfs(-1,1,s);
        cout << len << endl;
        if(len)//如果答案个数大于0,排序
        {
            sort(ans, ans +len);
            for(int i = 0; i <len; i++) cout << ans[i] <<" ";
            puts(" ");
        }   
    }
    return 0;
}

4.五指山

AcWing1299. 五指山
大圣在佛祖的手掌中。

我们假设佛祖的手掌是一个圆圈,圆圈的长为 n,逆时针记为:0,1,2,…,n−1,而大圣每次飞的距离为 d。

现在大圣所在的位置记为 x,而大圣想去的地方在 y。

要你告诉大圣至少要飞多少次才能到达目的地。

注意:孙悟空的筋斗云只沿着逆时针方向翻。

输入格式
有多组测试数据。

第一行是一个正整数 T,表示测试数据的组数;

每组测试数据包括一行,四个非负整数,分别为如来手掌圆圈的长度 n,筋斗所能飞的距离 d,大圣的初始位置 x 和大圣想去的地方 y。

输出格式
对于每组测试数据,输出一行,给出大圣最少要翻多少个筋斗云才能到达目的地。

如果无论翻多少个筋斗云也不能到达,输出 Impossible。

数据范围
2<n<109,
0<d<n,
0≤x,y<n
输入样例:

2
3 2 0 2
3 2 0 1

输出样例:

1
2
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int exgcd(LL a,LL b, LL &x,LL &y)//扩展欧几里得
{
    if(!b)//如果b为0
    {
        x = 1, y = 0;
        return a;
    }
    int d = exgcd(b, a % b, y , x);//d为a和b的最大公约数
    y -= a/b*x;
    return d;
}
int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        LL n, d, x, y, a,b;
        scanf("%lld%lld%lld%lld", &n, &d, &x, &y);
        
        int gcd= exgcd(n, d,a,b);
        if((y-x)%gcd) puts("Impossible");//若gcd不能整除y-x,则无解
        else
        {
            b *= (y - x)/gcd;
            n /= gcd;
            printf("%lld\n", (b%n + n)%n);//保证结果为正数
        }
    }
    return 0;
}

5.最大比例

1223. 最大比例
在这里插入图片描述
在这里插入图片描述

#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 110;
int n;
LL x[N], a[N], b[N];
LL gcd(LL a, LL b)
{
    return b ? gcd(b, a % b): a;
}

LL gcd_sub(LL a , LL b)
{
    if(a < b) swap(a, b);//保证b<a;
    if(b == 1) return a;
    return gcd_sub(b,a/b);
}
int main()
{
    cin>> n;
    for(int i = 0; i <n; i++) cin >> x[i];
    sort(x, x+n);//排序
    
    int cnt = 0;
    for(int i = 1; i <n; i++)
     if(x[i] != x[i  -1])//判重
     {
         LL d = gcd(x[i], x[0]);//最大公约数
         a[cnt] = x[i]/d;//分子
         b[cnt] = x[0]/d;//分母
         cnt++;
     }
     
     LL up = a[0], down = b[0];
     for(int i = 1; i <cnt; i++)//求当前分子分母的指数 的最大公约数
     {
         up = gcd_sub(up, a[i]);//更相减损术
         down = gcd_sub(down,b[i]);
     }
     cout << up  <<'/' << down << endl;
     return 0;
}

6.C 循环

AcWing1301. C 循环
在这里插入图片描述
输入样例:

3 3 2 16
3 7 2 16
7 3 2 16
3 4 2 16
0 0 0 0

输出样例:

0
2
32766
FOREVER

在这里插入图片描述

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y)
{
    if(b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    LL d = exgcd(b, a%b,y, x);
     y -= a/b*x;
     return d;
}
int main()
{
    LL a,b,c,k;
    while(cin >> a >> b >> c >>k, a||b ||c ||k)//逗号表达式,取最后一个表达式的值
    {
        LL x, y;
        LL z = 1ll << k;//2^k^
        LL d = exgcd(c,z,x,y);//最大公约数,扩展欧几里得算法
        if((b-a)%d) cout << "FOREVER"<< endl;
        else
        {
            x *= (b-a)/d;
            z /= d;
            cout << (x % z + z)%z << endl;
        }
    }
    return 0;
}

7.正则问题

AcWing1225. 正则问题
在这里插入图片描述
xx|xxx 表示要么是xx或者xxx
(xx|xxx)x表示要么是xxx或者xxxx, 括号外面的直接与里面相连

用 | : 表示两者取其一
用 &:表示两者相连
在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
string str;
int k;
int dfs()
{
    int res = 0;
    while(k < str.size())
    {
        if(str[k] == '(')
        {
            k++;//跳过‘(’;
            res += dfs();
            k++;//跳过')'
        }
        else if(str[k] == '|')
        {
            k++;//跳过‘|’
            res = max(res, dfs());
        }
        else if(str[k] == ')') break;
        else {
            k++;//跳过‘x’
            res++;
        }
    }
    return res;
}
int main()
{
    cin >> str;
    cout << dfs() << endl;
    return 0;
}

8.糖果

Acwing1243. 糖果
在这里插入图片描述
输入样例:

6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2

输出样例:

2

在这里插入图片描述

//大致顺序:先枚举可选择数最少的一列,然后再在这一列中枚举选择哪一行
//这样的时间复杂度应该是最低的,
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N = 110, M = 1<<20;
int n, m,k;
vector<int> col[M];//用来记录col中每一列可选择的行数有哪些
int log2[M];//预处理,方便计算 log2(2的n次方)
int lowbit(int x)
{
    return x & -x;
}
int h(int state)//当前状态所需最小行的数量
{
    int res = 0;
    for(int i = (1<<m) - 1 -state;i ; i -= lowbit(i)) //求最小方案数时,假设选择了某一列,则等价于选择了这一列的全部方案数
    {
        int c = log2[lowbit(i)];//i返回最后一位1,通过log2直接映射为最后一位1的位置
        res++;
        for(auto row : col[c]) i &= ~row;//row表示哪一列有1,每次选择一种方案,等价于将这种方案对应的位变为0,通过&操作实现
    }
    return res;
}
bool dfs(int depth, int state)// depth表示层数,state用于维护选择糖果过程中已经选择了哪些口味
{
    if(!depth || h(state) >depth)//如果层数已经没有了 或者 当前状态所需行的数量大于我们还能选的行的数量
    return state == (1 << m)-1;// (1<<m)-1表示m位全是一, 即2^m-1
    //找到当前选择性最少的一列
    int t = -1;//t是指向选择数最少的那一列的指针
    for(int i = (1 << m) - 1-state; i ; i -= lowbit(i))
    {
        int c = log2[lowbit(i)];
        if(col[t].size() > col[c].size() || t == -1 )
        t = c;
    }
    
    //枚举选哪行
    for(auto row:col[t])
    if(dfs(depth -1, state|row))
     return true;
     
     return false;
}
int main()
{
    cin >> n >>m >>k;
    for(int i = 0; i <m; i++) log2[1<<i] = i;
    for(int i = 0; i <n; i++)
    {
        int state = 0;
        //将该包糖果所包含的糖果对应的位数置为1
        for(int j = 0; j < k; j++)
        {
            int c;
            cin >> c;
            state |= 1 << c - 1;
        }
        
         //找出这包糖果 哪个位置可以填成1,将该列对应的col+1
        for(int j = 0; j <m; j++)
        if(state >> j &1)//若第j位有1
          col[j].push_back(state);
    }
    int depth = 0;
    while(depth <= m && !dfs(depth, 0)) depth ++;
    if(depth > m) depth = -1;//无解
    cout << depth << endl;
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值