数论

等差数列

题目大意
数学老师给小明出了一道等差数列求和的题目。
但是粗心的小明忘记了一部分的数列,只记得其中 N N N 个整数。
现在给出这 N N N 个整数,小明想知道包含这 N N N 个整数的最短的等差数列有几项?
输入格式
输入的第一行包含一个整数 N N N
第二行包含 N N N 个整数 A 1 , A 2 , ⋅ ⋅ ⋅ , A N A_1,A_2,⋅⋅⋅,A_N A1,A2,,AN。(注意 A 1 ∼ A N A_1∼A_N A1AN 并不一定是按等差数列中的顺序给出)
输出格式
输出一个整数表示答案。
数据范围: 2 ≤ N ≤ 100000 2≤N≤100000 2N100000 0 ≤ A i ≤ 1 0 9 0≤A_i≤10^9 0Ai109
输入样例

5
2 6 4 10 20

输出样例

10

样例解释:包含 2、6、4、10、20 的最短的等差数列是 2、4、6、8、10、12、14、16、18、20。
从第二项开始每一项与第一项的差一定是公差的倍数。

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 7;
int a[maxn],b[maxn];
int main()
{
    int n, x, d = 0;
    scanf("%d",&n);
    for(int i = 1; i <= n; i++)  scanf("%d",&a[i]);
    sort(a + 1, a + 1 + n);  //排序
    //求后面每一项与首项的差值的最大公约数
    for(int i = 2; i <= n; i++) d = __gcd(d, a[i] - a[1]);
    if(!d) printf("%d\n",n);  //公差为 0
    else printf("%d\n",(a[n]-a[1])/d+1);  //公差不为0
    return 0;
}

X的因子链

题目大意
输入正整数 X X X,求 X X X 的大于 1 的因子组成的满足任意前一项都能整除后一项的严格递增序列的最大长度,以及满足最大长度的序列的个数。
输入格式
输入包含多组数据,每组数据占一行,包含一个正整数表示 X X X
输出格式
对于每组数据,输出序列的最大长度以及满足最大长度的序列的个数。
每个结果占一行。
数据范围: 1 ≤ X ≤ 220 1≤X≤220 1X220
输入样例

2
3
4
10
100

输出样例

1 1
1 1
2 1
2 2
4 6
#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn = (1<<20) + 10;
int primes[maxn];
bool st[maxn];
int minp[maxn];
int sum[maxn],cnt;
void get_primes(int x)
{
    for(int i = 2; i <= x; i++)
    {
        if(!st[i])
        {
            primes[cnt++] = i;
            minp[i] = i;  //存最小质因子
        }
        for(int j = 0; primes[j] <= x/i; j++)
        {
            int t = primes[j]*i;
            minp[t] = primes[j];
            st[t] = true;
            if(i%primes[j] == 0) break;
        }
    }
}
int main()
{
    get_primes(maxn);
    int x, fact[30];  //fact[] 存最小质因子
    while(~scanf("%d",&x))
    {
        int tot = 0, p, idx = 0;  //tot: 质因子总出现次数
        while(x > 1)
        {
            p = minp[x], sum[idx] = 0, fact[idx] = p;
            while(x%p == 0) sum[idx]++, x/=p, tot++;
            ++idx;  //下标
        }
        ll res = 1;
        for(int i = 2; i<= tot; i++) res *= i;
        for(int i = 0; i < idx; i++)
           for(int j = 1; j <= sum[i]; j++) res /= j;
        printf("%d %lld\n",tot,res);
    }
    return 0;
}

聪明的燕姿

题目大意
城市中人们总是拿着号码牌,不停寻找,不断匹配,可是谁也不知道自己等的那个人是谁。
可是燕姿不一样,燕姿知道自己等的人是谁,因为燕姿数学学得好!
燕姿发现了一个神奇的算法:假设自己的号码牌上写着数字 S S S,那么自己等的人手上的号码牌数字的所有正约数之和必定等于 S S S
所以燕姿总是拿着号码牌在地铁和人海找数字(喂!这样真的靠谱吗)。
可是她忙着唱《绿光》,想拜托你写一个程序能够快速地找到所有自己等的人。
输入格式
输入包含 k k k 组数据。
对于每组数据,输入包含一个号码牌 S S S
输出格式
对于每组数据,输出有两行。
第一行包含一个整数 m m m,表示有 m m m 个等的人。
第二行包含相应的 m m m 个数,表示所有等的人的号码牌。
注意:你输出的号码牌必须按照升序排列。
数据范围: 1 ≤ k ≤ 100 , 1 ≤ S ≤ 2 × 1 0 9 1≤k≤100,1≤S≤2×10^9 1k100,1S2×109
输入样例

42

输出样例

3
20 26 41
/***
算术基本定理:
N = (P1)^(a1)(P2)^(a2)...(Pn)(an)
约数个数:(a1+1)(a2+1)...(an+1)
约数之和: (1+(p1)^(1)+...+(p1)^(a1))(1+(p2)^(1)+...+(p2)^(a2))(1+(pn)^(1)+...+(pn)^(an))
***/
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 7;
int primes[maxn];  //存素数
int ans[maxn];  //存答案
int cnt;  //素数表的个数
int idx;  //答案的下标
bool st[maxn];  //判断素数是否存在,true:筛去

void get_primes(int x)  //埃式筛法
{
    for(int i = 2; i <= x; i++)
    {
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= x/i; j++)
        {
            int t = primes[j]*i;
            st[t] = true;
            if(i%primes[j] == 0) break;  //primes[j] 是 i 的最小质因子
        }
    }
}

bool is_primes(int x)  //判断素数
{
    if(x < 2) return false;
    for(int i = 2; i <= x/i; i++)
        if(x%i == 0) return false;
    return true;
}

void dfs(int last, int now, int rest) //last:上次枚举质数的下标,now:当前值,rest:剩余值
{
    if(rest == 1)
    {
        ans[idx++] = now;
        return ;
    }
     //rest = 1 + p的特殊情形        ----- 时间复杂度仅为根号级别
    if(rest - 1 > (last < 0 ? 1 : primes[last]) && is_primes(rest - 1))
        ans[idx++] = now*(rest - 1);
    
    //rest = (1 + pi + pi ^ 2 + ···)(1 + pi + pi ^ 2 + pi ^3 ···)···的情形,pi ^ 2 <= rest
    for(int i = last + 1; primes[i] <= rest/primes[i]; i++)
         for(int j = 1 + primes[i], t = primes[i]; j <= rest; t *= primes[i], j += t)
            if(rest % j == 0) 
                dfs(i, now * t, rest / j);
}
int main()
{
    get_primes(maxn);
    int s;
    while(~scanf("%d",&s))
    {
        idx = 0;
        dfs(-1, 1, s);
        if(idx)
        {
            printf("%d\n",idx);
            sort(ans, ans + idx);
            for(int i = 0; i < idx; i++) printf("%d ",ans[i]);
            puts("");
        }
        else puts("0");
    }
    return 0;
}

五指山

题目大意
大圣在佛祖的手掌中。
我们假设佛祖的手掌是一个圆圈,圆圈的长为 n n n,逆时针记为: 0 , 1 , 2 , … , n − 1 0,1,2,…,n−1 0,1,2,,n1,而大圣每次飞的距离为 d d d
现在大圣所在的位置记为 x x x,而大圣想去的地方在 y y y
要你告诉大圣至少要飞多少次才能到达目的地。
注意:孙悟空的筋斗云只沿着逆时针方向翻。
输入格式
有多组测试数据。
第一行是一个正整数 T T T,表示测试数据的组数;
每组测试数据包括一行,四个非负整数,分别为如来手掌圆圈的长度 n n n,筋斗所能飞的距离 d d d,大圣的初始位置 x x x 和大圣想去的地方 y y y
输出格式
对于每组测试数据,输出一行,给出大圣最少要翻多少个筋斗云才能到达目的地。
如果无论翻多少个筋斗云也不能到达,输出 I m p o s s i b l e Impossible Impossible
数据范围: 2 < n < 109 , 0 < d < n , 0 ≤ x , y < n 2<n<109,0<d<n,0≤x,y<n 2<n<109,0<d<n,0x,y<n
输入样例

2
3 2 0 2
3 2 0 1

输出样例

1
2

扩展欧几里得

a x + b y = g c d ( a , b ) ax + by = gcd(a,b) ax+by=gcd(a,b)
对于任意正整数 a a a b b b,一定存在非 0 整数 x x x y y y,使得 a x + b y = g c d ( a , b ) ax + by = gcd(a,b) ax+by=gcd(a,b) 成立。
0 与 a a a 的最大公约数为 a a a
b b b == 0
a x + b y = a ax + by = a ax+by=a x x x = 1, y y y = 0

b b b != 0
a x + b y = d ax + by = d ax+by=d b y + ( a % b ) x = d by + (a \% b)x = d by+(a%b)x=d
b y + ( a − ( a / b ) ∗ b ) ∗ x = d by+(a- (a/b)*b)*x = d by+(a(a/b)b)x=d ( a x + b ( y − ( a / b ) x ) = d (ax + b(y-(a/b)x)=d (ax+b(y(a/b)x)=d

b x + ( a % b ) y = g c d ( a , b ) bx + (a\%b)y = gcd(a,b) bx+(a%b)y=gcd(a,b)
b x + ( a − ( a / b ) ∗ b ) ∗ y = g c d ( a , b ) bx + (a-(a/b)*b)*y=gcd(a,b) bx+(a(a/b)b)y=gcd(a,b) a y + b ( x − ( a / b ) y = g c d ( a , b ) ay+b(x-(a/b)y=gcd(a,b) ay+b(x(a/b)y=gcd(a,b)

线性同余方程

a x ≡ b ax ≡ b axb ( m o d (mod (mod m ) m) m) m ⇒ 模数 a x − m y = b ax - my = b axmy=b 令 y’ = y,存在一个整数 y y y使得 a x = m y + b ax = my + b ax=my+b a x + m y ′ = b ax + my' = b ax+my=b
性质: b b b 是 (a,m) 最大公约数的倍数。
a ∗ x % m = b a*x\%m = b ax%m=b ⇒ (x = b*a%m)x

#include <cstdio>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y) //扩展欧几里得算法 求 a,b的最大公约数
{
    if(b == 0)
    {
        x = 1, y = 0;
        return a;
    }
    ll gcd = exgcd(b, a%b, y, x);
    y -= a/b*x;
    return gcd;
}
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);
        ll gcd = exgcd(n, d, a, b);
        if((y-x)%gcd) puts("Impossible");
        else
        {
            b *= (y - x)/gcd;
            n /= gcd;
            printf("%lld\n",(b%n+n)%n);
        }
    }
    return 0;
}

最大比例

题目大意
X X X星球的某个大奖赛设了 M M M 级奖励。每个级别的奖金是一个正整数。并且,相邻的两个级别间的比例是个固定值。也就是说:所有级别的奖金数构成了一个等比数列。比如:16,24,36,54,其等比值为:3/2。现在,我们随机调查了一些获奖者的奖金数。请你据此推算可能的最大的等比值。
输入格式
第一行为数字 N N N ,表示接下的一行包含 N N N 个正整数。
第二行 N N N 个正整数 X i X_i Xi,用空格分开,每个整数表示调查到的某人的奖金数额。
输出格式
一个形如 A / B A/B A/B 的分数,要求 A 、 B A、B AB互质,表示可能的最大比例系数。
数据范围: 0 < N < 100 , 0 < X i < 1 0 12 0<N<100,0<X_i<10^{12} 0<N<1000<Xi<1012 数据保证一定有解。
输入样例

3
1250 200 32

4
3125 32 32 200

3
549755813888 524288 2

输出样例

25/4

5/2

4/1
/***
将一组数排序过后成为有序数列
a[i]/a[0] ... a[n-1]/a[0] (i >= 1 && i < n)
x[idx] = a[i]/d;
y[idx] = a[0]/d;
***/
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 110;
typedef long long ll;
ll a[maxn], x[maxn], y[maxn];
ll sub_gcd(ll x, ll y) //辗转相减
{
    if(x < y) swap(x,y);
    if(y == 1) return x;
    return sub_gcd(y,x/y);
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%lld",&a[i]);
    sort(a, a + n);
    int idx = 0;
    for(int i = 1; i < n; i++)
        if(a[i] != a[i-1])
        {
            ll d = __gcd(a[i], a[0]);
            x[idx] = a[i]/d;  //分子
            y[idx++] = a[0]/d;  // 分母
        }
    ll up = x[0], down = y[0];
    for(int i = 1; i < idx; i++)
    {
        up = sub_gcd(up, x[i]); //分子
        down = sub_gcd(down, y[i]);  //分母
    }
    printf("%lld/%lld\n",up,down);
    return 0;
}

C 循环

题目大意
对于 C C C 语言的循环语句,形如:

for (variable = A; variable != B; variable += C) statement;

请问在 k k k 位存储系统中循环几次才会结束。
若在有限次内结束,则输出循环次数。否则输出死循环。
输入格式
多组数据,每组数据一行四个整数 A , B , C , k A,B,C,k A,B,C,k
读入以 0 0 0 0 结束。
输出格式
若在有限次内结束,则输出循环次数。
否则输出 F O R E V E R FOREVER FOREVER
数据范围: 1 ≤ k ≤ 32 , 0 ≤ A , B , C < 2 k 1≤k≤32,0≤A,B,C<2k 1k32,0A,B,C<2k
输入样例

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

输出样例

0
2
32766
FOREVER
/***
 (A + xC) % 2^k = B
 A + x*C - y*(2^k) = B
 x*C - y*2^k = B - A  ==> ax + by = d
***/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#define ll long long int
#define rep(i,a,b) for(auto i = (a); i <= (b); i++)
#define per(i,a,b) for(auto i = (a); i >= (b); i--)
using namespace std;
const int maxn = 5e6 + 7;
inline ll read() //快读
{
    ll s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return s*w;
}
inline void out(ll a) //快写
{
    if(a>=10)out(a/10);
    putchar(a%10+'0');
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
    if(!b)
    {
        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(A = read(), B = read(), C = read(), K = read())
    {
        if(!A && !B && !C && !K) 
        {
            break;
        }
        ll x, y, Z = 1ll << K;
        int d = exgcd(C, Z, x, y);
        if((B-A)%d) puts("FOREVER");
        else
        {
            x *= (B-A)/d;
            Z /= d;
            printf("%lld\n",(x%Z+Z)%Z);
        }
    }
    return 0;
}

正则问题

题目大意
考虑一种简单的正则表达式:
只由 x ( ) | 组成的正则表达式。
小明想求出这个正则表达式能接受的最长字符串的长度。
例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是6。
输入格式
一个由x()|组成的正则表达式。
输出格式
输出所给正则表达式能接受的最长字符串的长度。
数据范围:输入长度不超过100,保证合法。
输入样例

((xx|xxx)x|(x|xx))xx 

输出样例

6

|:取左右最长的 x
&:连接 x

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
string s;
int cnt;
int dfs()
{
    int res = 0, len = s.size();
    while(cnt < len)
    {
        if(s[cnt] == '(')  //处理(...)
        {
            cnt++;  //跳过'('
            res += dfs();
            cnt++;   //跳过 ')'
        }
        else if(s[cnt] == '|')
        {
            cnt++; //跳过'|'
            res = max(res, dfs());  //找左右最长的 'x'
        }
        else if(s[cnt] == ')') break;
        else if(s[cnt] == 'x')
        {
            cnt++;  //跳过'x'
            res++;
        }
    }
    return res;
}
int main()
{
    cin >> s;
    cout<<dfs()<<endl;
    return 0;
}

糖果

题目大意
糖果店的老板一共有 M M M 种口味的糖果出售。
为了方便描述,我们将 M M M 种口味编号 1 ∼ M 1∼M 1M
小明希望能品尝到所有口味的糖果。
遗憾的是老板并不单独出售糖果,而是 K K K 颗一包整包出售。
幸好糖果包装上注明了其中 K K K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 N N N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入格式
第一行包含三个整数 N , M , K N,M,K N,M,K
接下来 N N N 行每行 K K K 这整数 T 1 , T 2 , ⋅ ⋅ ⋅ , T K T_1,T_2,⋅⋅⋅,T_K T1,T2,,TK,代表一包糖果的口味。
输出格式
一个整数表示答案。
如果小明无法品尝所有口味,输出 −1。
数据范围: 1 ≤ N ≤ 100 , 1 ≤ M , K ≤ 20 , 1 ≤ T i ≤ M 1≤N≤100,1≤M,K≤20,1≤T_i≤M 1N100,1M,K20,1TiM
输入样例

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 <cstring>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 110, M = 1<<20;
vector<int>col[N];//用来记录col中每一列可选择的行数有哪些
int n, m, k;
int log2[M];//预处理,方便计算 log2(2的n次方)
int lowbit(int x)
{
    return x& -x;
}
int h(int state) //编写估价函数,看这时的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 = i &~ row; //row表示哪一列有1,每次选择一种方案,等价于将这种方案对应的位变为0,通过&操作实现
    }
    return res;
}
bool dfs(int depth,int state)// depth表示层数,state用于维护选择糖果过程中已经选择了哪些口味
{
    if(!depth || h(state) > depth)
    {
        //若可选择的方案为0或者最小需要选择的方案数都小于当前可选的方案数的话,则判断是否合法
        //判断方法:看state是否全为1
        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 (t == -1 || col[t].size() > col[c].size())
            t = c;
    }
    for(auto row: col[t])  //接下来枚举选择哪一行
        if(dfs(depth - 1, state|row)) return true;
    return false;
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    //预处理log2
    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 x;
            scanf("%d", &x);
            state = state|1<<(x - 1);
        }
        for(int j = 0;j < m; j++)  //找出这包糖果 哪个位置可以填成1,将该列对应的col+1
            if(state >> j & 1)//若第j位有1
                col[j].push_back(state);
    }
    int depth = 0;
    while(!dfs(depth, 0) && depth <= m) depth++;
    if(depth > m) depth = -1;
    printf("%d\n",depth);
    return 0;
}
/***
   算法:顺序枚举每一包糖果,然后枚举每一个状态,然后用糖果的状态去获得
   新状态,并且更新状态里的最少包数。
   注意事项:枚举状态的时候,一定要去掉不合法的状态。 
***/
#include <bits/stdc++.h>
using namespace std;
int dp[(1<<20)+1], a[110];
int n,m,k,x;
int main()
{
    memset(dp, -1, sizeof dp);
    memset(a, 0, sizeof a);
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0; i < n; i++)
        for(int j = 0; j < k; j++) scanf("%d",&x), a[i] |= 1 << (x-1);  //几种口味的糖
    
    
    int lim = 1 << m; dp[0] = 0;
    for(int i = 0; i < n; i++)
    {
        for(int st = 0; st < lim; st++)
        {
            if(dp[st] == -1) continue;
            int now = st | a[i];
            if(dp[now] == -1 || dp[now] > dp[st] + 1) dp[now] = dp[st] + 1;
        }
    }
    printf("%d\n",dp[lim-1]);
    return 0;
}

这个题解决方案还是状压dp好理解, A* 算法QwQ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

幸愉聊信奥

谢谢亲的支持,我会继续努力啦~

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

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

打赏作者

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

抵扣说明:

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

余额充值