【ACM】kuangbin基础数论专题

知识补充:裴蜀定理

1.Bi-shoe and Phi-shoe

1.题意

给出一行数,求对应的数的欧拉函数值大于给出的数的数的最小和。

2.思路

我们知道素数表的欧拉函数值递增。
设素数a,b,b是a的下一个素数,则phi[a]<ph[b],且a-b间的所有数的欧拉函数都小于等于phi[a]。
欧拉函数值大于x的数的最小的数就是大于x的最小的素数

3.代码
#include"bits/stdc++.h"
using namespace std;
typedef long long LL;
const int N = 1001005;
int prime[N],cnt;
bool st[N];
int c=1;
void Prime(int n){
    st[0] = st[1] = 1;
    for(int i = 2; i <= n; ++i){
        if(!st[i]) prime[cnt++]=i;
        for(int j = 0; prime[j]<= n/i; ++j){
            st[i*prime[j]] = 1;
            if(i%prime[j]==0) break;
        }
    }
}
int main(){
    int t;
    cin >> t;
    Prime(N);
    while(t--){
        int n;
        cin >> n;
        LL ans=0;
        for(int i = 0; i < n; ++i){
            int x;cin >> x;
            for(int j = x+1; ;++j){
                if(!st[j]) { //如果是素数
                    ans+=j;
                    break;
                }
            }
        }
        printf("Case %d: %lld Xukha\n",cnt++,ans);
    }
    return 0;
}

2.Prime Independence

1.题意
2.思路
3.代码

3.Aladdin and the Flying Carpet

1.题意

给出整数 a 和 b ,求区间[b, a] 内的 a 的约数对的个数,即:满足c*d == a 且 c>=b,d>=b。a 的约数对(比如[2, 5] 与 [5, 2] 为同一对)。

2.思路

线性筛+唯一分解定理
根据唯一分解定理,先将a唯一分解,则a的所有正约数的个数为num = (1 + a1) * (1 + a2) *…(1 + ai),这里的ai是素因子的指数,见唯一分解定理,因为题目说了不会存在c==d的情况,因此num要除2,去掉重复情况,然后枚举小于b的a的约数,拿num减掉就可以了。

3.代码
#include"bits/stdc++.h"
#define s(a) scanf("%d",&a)
#define sl(a) scanf("%lld",&a)
#define sl2(a,b) scanf("%lld%lld",&a,&b)
using namespace std;
typedef long long ll;
const int MAXN = 1000010;
int prime[MAXN];
bool vis[MAXN];
int t;
int cnt=1;
int num=0;
void Prime(int n){
	memset(vis,0,sizeof(vis));
	vis[0] = vis[1] = 1;
	for(int i = 2; i <= n; ++i){
		if(!vis[i]) prime[cnt++] = i;
		for(int j = 1; j <= cnt && i*prime[j]<=n ; ++j){
			vis[i*prime[j]] = 1; //prime[j]是素数,他的倍数也是素数 
			if( i%prime[j] == 0) break; //i是某个素数的倍数,直接跳出 
		}
	}
}
ll getfac(ll x)
{
    ll ans=1;
    for(int i=1;prime[i]<=x/prime[j];i++)
    {
        ll sum=0;//当前质因数的幂指数
        while(x%prime[i]==0)//当是这个数的因子时
        {
            sum++;
            x/=prime[i];
        }
        ans*=(sum+1);//应用定理的结论
    }
    if(x>1)//当搜索不到的时候,如果这个数最后大于一,那么这个最后结果肯定是素数,并且指数是1
    ans*=2;
    return ans;
}

int main(){
	Prime(MAXN);
	s(t);
	while(t--) {
		ll a, b;
		scanf("%lld%lld",&a,&b);
		if(a<b*b) {
			printf("Case %d: 0\n",++num);
			continue;
		}
		ll ans = getfac(a)/2;
		for(ll i = 1; i < b; ++i)
			if(a%i==0) ans--;
		printf("Case %d: %lld\n",++num,ans);
	}
	return 0;
}

4.Sigma Function

1.题意

求从1-n的数中约数和是偶数的数的个数

2.思路

1-100打表
不满足的数/和
1 1
2 3
4 7
8 15
9 13
16 31
18 39
25 31
32 63
36 91
49 57
50 93
64 127
72 195
81 121
98 171
100 217

发现100中,因子和为奇数的有:

1 2 4 6 9 16 18 25 32 36 49 50 64 72 81 98 100

有没有发现,这些数字有一个规律,他们是 x^2, 2x, 2x^2, 只要100中的数满足这三个中的一个,那么,这个数就是不满足的,

总数-不满足的个数 = 满足的个数

我们还可以发现:
当x为偶数时2x和x^2会有重复的部分
当x为奇数时2
x和2*x^2会有重复的部分
最后发现x^2和 2x^2涵盖了所有情况,且不重复,因此减去这两中情况的数目即可

3.代码
#include "bits/stdc++.h"
#define s(a) scanf("%d",&a)
#define sl(a) scanf("%lld",&a)
#define s2(a,b) scanf("%d%d",&a,&b)
#define sl2(a,b) scnaf("%lld%lld",&a,&b)
using namespace std;
typedef long long ll;
int cnt=1;
int main(){
    int t;
    cin>>t;
    while(t--){
        ll n;
        cin>>n;
        ll ans=0;
        ll x = sqrt(n);
        ll y = sqrt(1.0*n/2);
        ans=n-x-y;
        printf("Case %d: %lld\n",cnt++, ans);
    }
    return 0;
}

5.Leading and Trailing

1.题意

求n^k前三位和后三位

2.思路

1.暴力跑肯定超时
2.后三位好求,n^k%1000即可
前三位需要变形:参考大佬的推导
在这里插入图片描述
接下来我们求k+lgn即可
怎么求呢,有个函数学习一下modf()

求出小数部分b后,计算一下就好啦,不过要注意最后要输出3位,需要控制一下输出格式,当n^k=10000000时,对1000取余结果为0,需要再补2个0

3.代码
    #include "bits/stdc++.h"
    #define s(a) scanf("%d",&a)
    #define sl(a) scanf("%lld",&a)
    #define s2(a,b) scanf("%d%d",&a,&b)
    #define sl2(a,b) scanf("%lld%lld",&a,&b)
    using namespace std;
    typedef long long ll;
    int t, cnt=1;
    double x,y;
    ll n,k;
    ll quick_mod(ll a,ll b,ll p){
        ll num=a,sum=1;
        while(b){
            if(b&1) sum=sum*num%p;
            num=num*num%p;
            b>>=1;
        }
        return sum;
    }///a^b mod p*/
    int main(){
        //freopen("d://data.txt" ,"w" ,stdout);
        cin>>t;
        while(t--){
            cin>>n>>k;
            y = modf((double)(k*log10(n)),&x);
            ll ans1 = pow(10,y)*100;
            ll ans2=quick_mod(n,k,1000);
            printf("Case %d: %03lld %03lld\n",cnt++, ans1,ans2);
        }
        return 0;
    }
//注意输出格式,后三位可能存在最大位为十位或个位,需要补0

6.Goldbach`s Conjecture

1.题意

求n=a+b且a,b都是素数这样的a,b的对数

2.思路

素数筛模板题
遍历素数表prime[i],若n-prime[i]也是素数,ans++
prime[i]>n/2时结束,因为后面再跑就重复了嘛

3.代码
#include "bits/stdc++.h"

#define s(a) scanf("%d",&a)
#define sl(a) scanf("%lld",&a)
#define s2(a, b) scanf("%d%d",&a,&b)
#define sl2(a, b) scanf("%lld%lld",&a,&b)
using namespace std;
typedef long long ll;
int t, c = 1;
const int MAXN = 1e7+10;
int prime[1000000];
bool vis[MAXN];
int cnt = 0;
void Prime(int n) {

    memset(vis, 0, sizeof(vis));
    vis[0] = vis[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!vis[i]) prime[cnt++] = i;
        for (int j = 0; j < cnt && i * prime[j] <= n; ++j) {
            vis[i * prime[j]] = 1; //prime[j]是素数,他的倍数也是素数
            if (i % prime[j] == 0) break; //i是某个素数的倍数,直接跳出
        }
    }
}

int main() {
    //freopen("d://data.txt" ,"w" ,stdout);
    Prime(MAXN);
    cin >> t;
    while (t--) {
        int n,ans=0;
        cin >> n;
        for(int i =0; i < cnt; ++i){
            if(prime[i]>n/2) break;
            if(!vis[n-prime[i]]) ans++;
        }
        printf("Case %d: %d\n",c++, ans);
    }
    return 0;
}

7.Harmonic Number (II)

1.题意

求n*(1+1/2+1/3+…+1/n)

2.思路

分块数论

3.代码
#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
int t,cnt= 1;
int main(){
    scanf("%d",&t);
    while(t--){
        ll n,sum = 0;
        scanf("%lld",&n);
        for(ll l=1,r;l<=n; l=r+1){
            r = n/(n/l);
            sum+=n/l*(r-l+1);
        }
            
        printf("Case %d: %lld\n",cnt++,sum);
    }
    return 0;
}
//对称点(sqrt(n),sqrt(n)),求一半面积乘2

//分块数论:https://blog.csdn.net/weixin_43627118/article/details/104024380?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.edu_weight

8.Pairs Forming LCM

1.题意

在a,b中(a,b<=n),有多少组(a,b) (a<b)满足lcm(a,b)==n;

2.思路

素因子分解:n = p1 ^ e1 * p2 ^ e2 *…*pn ^ en
取n的两个因子a,b
a=p1 ^ a1 * p2 ^ a2 *…*pn ^ an
b=p1 ^ b1 * p2 ^ b2 *…*pn ^ bn

**gcd(a,b)=p1 ^ min(a1,b1) * p2 ^ min(a2,b2) *…*pn ^ min(an,bn)
lcm(a,b)=p1 ^ max(a1,b1) * p2 ^ max(a2,b2) pn ^ max(an,bn)

分解n,n = p1 ^ e1 * p2 ^ e2 *…*pk ^ ek,

lcm(a,b)=p1 ^ max(a1,b1) * p2 ^ max(a2,b2) *…*pk ^ max(ak,bk)

所以,当lcm(a,b)==n时,max(a1,b1)==e1,max(a2,b2)==e2,…max(ak,bk)==ek

当ai == ei时,bi可取 [0, ei] 中的所有数 有 ei+1 种情况,bi==ei时同理。

那么就有2(ei+1)种取法,但是当ai = bi = ei 时有重复,所以取法数为2(ei+1)-1=2ei+1。
除了 (n, n) 所有的情况都出现了两次 那么满足a<=b的有 (2
ei + 1)) / 2 + 1 个,+1的情况是a = b的时候
参考dl

3.代码
#include <bits/stdc++.h>
using namespace std;
int k;
long long suv[100500];
const int N=10000100;
long long prime[N/10]={0};
int num_prime=0;
bool isNotPrime[N]={1,1};
void su(){
    for(long i = 2 ; i < N ; i ++){
        if(!isNotPrime[i])
        prime[num_prime++]=i;
        for(long j = 0 ; j < num_prime &&i*prime[j]<N ;j ++) {
            isNotPrime[i * prime[j]] = 1;
            if( !(i % prime[j]))break;
        }
    }
}
void prime_solve(long long n){
    memset(suv,0,sizeof(suv));
    for(long long i=0;i<num_prime&&prime[i]*prime[i]<=n;i++){
//        cout<<prime[i]<<endl;
        if(n%prime[i]==0){
            while(n%prime[i]==0){
                n/=prime[i];
                suv[k]++;
            }
            //cout<<prime[i]<<" "<<suv[k]<<endl;
            k++;
        }
    }
    if(n!=1)suv[k++]=1;
}//用这种分解方法比较快

int main(){
    int t;
    scanf("%d",&t);
    int m=1;
    su();
    while(t--){
        long long n;
        scanf("%lld",&n);
        k=0;
        prime_solve(n);
        long long sum=1;
        for(int i=0;i<k;i++)
        sum*=(2*suv[i]+1);
        printf("Case %d: %lld\n",m++,(sum+1)/2);
    }
    return 0;
}
//https://blog.csdn.net/acvay/article/details/47333871

9.Harmonic Number

1.题意

输入n,求调和级数在这里插入图片描述
在这里插入图片描述

2.思路

结论题。
当n很小的时候,前缀和预处理,O(1)访问
当n很大的时候,调和级数的近似和为:f(n)=ln(n)+C+1/(2*n);
C为欧拉常数
C = 0.57721566490153286060651209

3.代码
#include"iostream"
#include"cmath"
#include"cstdio"
using namespace std;
const int N = 100005;
const double r = 0.57721566490153286060651209;//欧拉常数
double a[N];
int main(){
    for(int i = 1; i <= N; ++i)
        a[i] = a[i-1] + 1.0/i;
    int t, cnt = 0;
    cin >> t;
    while(t--){
        int n;
        cin >> n;
        if(n <= N) printf("Case %d: %.10f\n",++cnt, a[n]);
        else printf("Case %d: %.10f\n",++cnt, log(n) + r + 1.0/(2*n));
    }
    return 0;
}

10.Mysterious Bacteria

1.题意

x=b^p 给出x求出最小的p

2.思路

开始想两层循环暴力跑,底数从1~2^16(65536),幂从1到32,后来发现不对,大于1e7的样例就跑不出来了。

后来又想用唯一分解定理写,以为要求最大的pi,发现还是不对
补充知识:任何一个数都可以分解成如下形式:x = p1x1*p2x2*p3x3*…*psxs,pi为质数

比如:24 = 2^3 * 3^1
但这里答案并不是3,因为题目要求最后的底数就一个b,所以需要能合并。
再如:324 = 3^4 * 2^2,发现p应该是gcd(4, 2) = 2,即324 = 18^2
因此得出p=gcd(x1,x2,x3,x4…,xk)

还有一个坑点就是n可以是负数,此时需要转化为正数来求。

如果p是偶数,需要做修正,因为-16 != -2^4,所以,需要除2使其变成奇数,比如-324 = -(18^2) = -324^1
如果p是奇数,一个数的奇数次幂可以是负数,则不需要调整。

参考网友

3.代码

错误代码

#include"iostream"
#include"cmath"
#include"cstdio"
using namespace std;
typedef long long LL;
const int N = 65536;
int main(){
    int t, cnt = 0;
    cin >> t;
    
    while(t--){
        LL n;
        cin >> n;
            bool f = 0;
            for(int i = 1; i <= N; ++i){
                LL p = 1;
                for(int j = 1; j <= 32; ++j){
                    p*=i;
                    if( p == n) {
                        printf("Case %d: %d\n",++cnt, j);
                        f = 1;break;
                    }
                }
                if(f) break;
            }
    }
    return 0;
}


AC代码

#include"iostream"
#include"cmath"
#include"cstdio"
using namespace std;
typedef long long LL;
const int N = 1e5+10;
int prime[N],cnt;
bool st[N];
LL gcd(LL a, LL b)
{
    return b ? gcd(b, a % b) : a;
}
void Prime(int n){ //线性筛
    for(int i = 2; i <= n; ++i){
        if(!st[i]) prime[ cnt ++] = i;
        for(int j = 0; prime[j] <= n/i; ++j){
            st[prime[j] * i] = 1;
            if(i % prime[j] == 0) break;
        }
    }
}
int main(){
    Prime(N);
    int t, c = 0;
    cin >> t;
    
    while(t--){
        LL n;
        bool f= 0;  //标记n的正负
        cin >> n;
        
        if(n < 0) 
            n = -n, f = 1;	//n是负数,标记一下,后面做修正
        int p = 0;
        for(LL i = 0; prime[i] <= n/prime[i]; ++i){ //素数分解定理
            if(n % prime[i] ==0){
                LL s = 0;
                while(n %prime[i] == 0){
                    n/=prime[i];
                    s++;
                }
                if(p == 0) p = s;
                else p = gcd(p, s);
            }
        }
        if(n > 1) p = gcd(p, 1);
        if(f)  while( p%2 == 0) p >>= 1;  //如果n是负数,那么p只能是奇数,因为一个数的偶次幂必然是正数,所以作除2操作将其变成奇数
        printf("Case %d: %lld\n",++c, p);
    
    }
    return 0;
}

11.Large Division

1.题意

大数取余

2.思路

大数除法模板题

3.代码
#include"iostream"
#include"cmath"
#include"cstdio"
#include"unordered_map"
#include"vector"
using namespace std;
typedef long long LL;
// A / b = C ... r, A >= 0, b > 0
int main(){
    int t, c = 0;
    scanf("%d",&t);

    while(t--){
        string a;
        LL b,r;
        cin >> a >> b;
        if(b < 0) b = -b;
        vector<int>A;
        for(int i = a.size()-1; i >=0 ; --i) 
            if(a[i] == '-') continue;
            else A.push_back(a[i]-'0');
        vector<int> C;
        int r = 0;
        for (int i = A.size() - 1; i >= 0; i -- )
        {
            r = r * 10 + A[i];
            C.push_back(r / b);
            r %= b;
        }
        if(r == 0)
            printf("Case %d: divisible\n",++c);
        else
            printf("Case %d: not divisible\n",++c);
    }
    return 0;
}

Python直接写

# Python代码
t = int(input())
c = 1
while t:
    if t == 0:
        break
    a, b = map(int, input().split())
    if a % b == 0:
        print("Case " + str(c) + ": divisible")
    else:
        print(print("Case " + str(c) + ": not divisible"))
    t = t - 1
    c = c + 1

12.Fantasy of a Summation

1.题意

优化题目中的代码,题目中复杂度为n^k

2.思路

找规律,写几组样例即可发现一共有kn^k个数,而且每个数出现的次数是一样的,那么每个数出现的次数就是kn ^(k-1),那么答案:ans =∑(i从1到n) a[i] * k * n ^(k-1) = sum * k * n ^(k-1); 用快速幂求即可。

3.代码
#include"iostream"
using namespace std;
typedef long long LL;
int cnt = 0;
LL qp(LL a, LL b, LL p){
    LL res = 1;
    while(b){
        if( b&1 ) res = res * a %p;
        b>>=1;
        a = a * a % p;
    }
    return res;
}
int main(){
    int t;
    cin >> t;
    while(t--){
        LL n, k, p;
        scanf("%lld%lld%lld", &n, &k, &p);
        LL sum = 0;
        for(int i = 1; i <= n; ++i){
            LL x; scanf("%lld",&x);
            sum += x;sum %= p;
        }
        sum = (sum * k) % p * qp(n,k-1, p) % p;
        printf("Case %d: %lld\n",++cnt,sum);
    }
    return 0;
}

13.Help Hanzo

1.题意

求a到b的素数的个数

2.思路

二次筛法

这里的区间范围给定的最大值是2^31 - 1,而用线性筛法求的是[1,n]中的所有质数,因此直接用线性筛法求肯定会直接gg,因此需要通过挖掘某些性质,才能有技巧性的完成。

在这里插入图片描述
参考来源

注意点:
1.筛[a,b]之间的素数时,要从第一个大于a并且是素数倍数的数开始,当a,b比较小的时候,可能[a,b]中包含素数p,因此:max((a+p-1)/p * p, 2*p)
2.需要注意偏移量,因为b-a<=1e5,那么s标记数st开到1e5就可以(我们这里开到1e6是因为要筛出2^16以内的素数),如果不做偏移量修正,那么可能需要访问到st[1e9],显然数组开不到这么大。

3.代码
#include "iostream"
#include "cmath"
#include "cstring"
using namespace std;
typedef long long LL;
const int N = 1000010;
int c = 0;
int prime[N],cnt;
bool st[N];

void Prime(int n){
    memset(st, 0 ,sizeof st);
    cnt = 0;
    for(int i = 2; i<= n; ++i)
    {
        if(!st[i]) prime[cnt ++] = i;
        for(int j = 0; prime[j] <= n/i; ++j){
            st[prime[j] * i] = 1;
            if( i % prime[j] == 0) break;
        }
    }
}
int main(){
    Prime(N);
    int t;
    scanf("%d",&t);
    while(t--){
        memset(st, 0, sizeof st);
        LL a,b;
        scanf("%lld%lld", &a, &b);
            for(int i = 0; i < cnt; ++i){
                int p = prime[i];
                //筛去[a,b]的合数
                for(LL j = max((a+p-1)/p*p, 2ll*p); j <= b; j+=p) st[j - a] = 1;
            }
            cnt = 0;
            for(int i = 0; i <= b-a; ++i)
                if(i+a > 1 && !st[i]) ++cnt;
            printf("Case %d: %d\n", ++c, cnt);
    }
    return 0;
}

14.Trailing Zeroes (III)

1.题意

输入q,代表N!末尾有q个0,求出最小的N

2.思路

打表到20!发现:
0-4:末尾无0
5-9:末尾1个0
10-14:末尾2个0
15-19:末尾3个0

猜测N每+5,末尾就多1个0
但是这样显然求不到q = 1e8时的N

重新思考:
N!末尾的0一定是由2*5产生的。
而且2因子的个数一定比5因子的个数多。
所以只需要求N!的5因子的个数。
用到了一个数论知识:
若p是质数,p<=n,则n!是p的倍数,设p^x是p在n!内的最高幂,则
x=[n/p]+[n/p^2]+ [n/p^3]+…;
而且[n/(ab)]=[[n/a]/b]
参考来源

3.代码
#include"iostream"
using namespace std;
int c;
const int N = 5e8;
int find(int x){
	int sum = 0;
	while(x){
		sum += x/5;
		x/=5; 
	} 
	return sum;
}
int check(int x){
	int l=1,r=N;
		while(l <= r){
			int mid = (l+r) >>1;
			int t = find(mid);
			if(t < x) l = mid + 1;
			else if(t > x) r = mid - 1;
			else return mid; 
		}
	return -1;
}
int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		int x;
		scanf("%d", &x);
		printf("Case %d: ",++c);
		int p = check(x);
		if(p==-1) puts("impossible");
		else printf("%d\n",p-p%5); //求出最小的满足条件的N,比如这里q=1时,求出的答案是7,需要ans-ans%5 
	}		
	return 0;
} 

15.GCD - Extreme (II)

1.题意

找出一个数的阶乘的末尾0个数是q

2.思路
3.代码

16.Code Feat

1.题意
2.思路
3.代码

17.Emoogle Grid

1.题意
2.思路
3.代码

18.青蛙的约会

1.题意

青蛙A,B在一条环形线上,抽象成数轴,长度为l,起始坐标分别为x、y,单位跳跃距离分别为n,m,问最少走几次l能相遇,若不能相遇输出impossible

2.思路

参考来源
在这里插入图片描述

3.代码
#include<cstdio>
#define LL long long
LL x,y,m,n,l,a,b,c,x0,y0,g,tmp;
void exgcd(LL a,LL b){
	if(!b){x0=1;g=a;return;}//顺便求gcd
	exgcd(b,a%b);
	tmp=x0;x0=y0;y0=tmp-a/b*y0;
}
int main(){
	scanf("%lld%lld%lld%lld%lld",&x,&y,&m,&n,&l);
	a=n-m;b=l;c=x-y;
	if(a<0)a=-a,c=-c;//处理a为负数情况
	exgcd(a,b);
	if(c%g)puts("Impossible");
	else printf("%lld\n",(c/g*x0%(b/g)+b/g)%(b/g));//求最小非负整数解
	return 0;
}

19.C Looooops

1.题意

求a+bx≡c(mod 2^k)

2.思路

题目的意思就是 a+bx≡c(mod 2^k)
典型的线性同余方程 转化为 bx-y2^k=c-a 当gcd(b,2^k)能整除c-a的时候 就存在解
通解好求 跑一边exgcd可以得出x0 然后 基础解 x=(c-a)/gcd(b,2^k)x0; 通解为 x0+zz(2^ k/gcd(2^k,b); 显然当zz=0的时候最小
关键是要求出最小正整数解。
对于ax+by=gcd(a,b) 这个方程 我们有通解 x=x0+b/gcd(a,b);
那么怎么求x的最小正整数解。呢 首先知道一点 b/gcd(a,b)是解的最小区间
这个怎么理解呢
假设c为x的解的最小间距,此时d为y的解的间距,所以x=x0+ct,y=y0-d*t(x0,y0为一组特解,t为任意整数)

  带入方程得:a*x0+a*c*t+b*y0-b*d*t=n,因为a*x0+b*y0=n,所以a*c*t-b*d*t=0,t不等于0时,a*c=b*d

  因为a,b,c,d都为正整数,所以用最小的c,d,使得等式成立,ac,bd就应该等于a,b的最小公倍数a*b/gcd(a,b),

  所以c=b/gcd(a,b),d就等于a/gcd(a,b)。

所以,若最后所求解要求x为最小整数,那么x=(x0%(b/gcd(a,b))+b/gcd(a,b))%(b/gcd(a,b))即为x的最小整数解。

x0%(b/gcd(a,b))使解落到区间-b/gcd(a,b) ~ b/gcd(a,b),再加上b/gcd(a,b)使解在区间0~2*b/gcd(a,b),

再模上b/gcd(a,b),则得到最小整数解
参考链接

3.代码
#include "iostream"
#include "cstdio"
using namespace std;
typedef long long LL;
LL a,b,c,k;

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(){
    while(scanf("%lld%lld%lld%lld",&a,&b,&c,&k),a+b+c+k){
        LL x,y;
        LL p = b-a;
        LL z = 1ll << k;
        LL d = exgcd(c,z,x,y);
        if(p%d) puts("FOREVER");
        else{
            LL t = z/d;
            x = (x*(p/d)%t+t)%t;
            printf("%lld\n",x);
        }
    }

    return 0;
}

20.Death to Binary?

1.题意
2.思路
3.代码

21.Primes

1.题意

1-16000,是素数输出yes,否则no

2.思路

素数筛筛一遍1-16000
注意1,2要输出no,n<=则结束输出

3.代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 20010;
int c=0;
int prime[N],cnt;
bool st[N];
void Prime(int n){
    st[1] = 1;
    for(int i = 2; i <= n; ++i){
        if(!st[i]) prime[cnt ++] = i;
        for(int j = 0; prime[j] <= n/i;++j){
            st[prime[j] * i] = 1;
            if( i % prime[j] ==0) break;
        }
    }
    st[2]=1;
}
int main()
{
    Prime(N);
    int n;
    while(cin >> n&&n>0) {
        printf("%d: ",++c);
        if(st[n]) puts("no");
        else puts("yes");
    }
    return 0;
}

22.Maximum GCD

1.题意

n个数中选2个,求gcd,找出最大的gcd

2.思路

暴力跑
输入很花哨,用getline

3.代码
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int n,num,a[110];
string s;
int gcd(int a,int b)
{
    return b?gcd(b, a%b):a;
}
int main()
{
    cin>>n;
    getchar();
    while(n--)
    {
        num=0;
        getline(cin,s);
        stringstream ss(s);
        while(ss>>a[num]) num++; //控制读入
        int ans=0;
        for(int i=0;i<num;i++)
            for(int j=i+1;j<num;j++)
                ans=max(ans,gcd(a[i],a[j]));
        cout<<ans<<endl;
    }
    return 0;
}

23.Prime Time

1.题意

对于fn = n^2+ n + 41,n属于[l,r],求素数的概率

2.思路

跑一下就可
这题结果要加1e-8
前缀和打表,否则超时

3.代码
#include "iostream"
#include "cstdio"
using namespace std;
const int N = 100010;
int sum[N];
bool isPrime(int n){
    for(int i = 2; i <= n/i; ++i)
        if(n%i==0) return 0;
    return 1;
}
void init(){
	int num;
	for(int  i = 0; i < N; ++i){
		int f = i*i+i+41;
		if(isPrime(f)) num++;
		sum[i] = num;
	}
}
int main(){
    int l,r;
	double ans;
    init();
    while(~scanf("%d%d",&l,&r)){
		if(l==0) ans = 100.00*sum[r]/(r-l+1);
		else ans = 100.00*(sum[r]-sum[l-1])/(r-l+1);
        printf("%.2f\n",(double)ans+1e-8);
    }
    return 0;
}

24.The equation

1.题意

线性方程区间求解的个数

2.思路

参考

3.代码
#include"bits/stdc++.h"
using namespace std;
typedef long long LL:
LL a,b,c,x1,x2,y1,y2,x,y;
inline long long cmin(const long long &x,const long long &y) {return x<y?x:y;}
inline long long cmax(const long long &x,const long long &y) {return x>y?x:y;}
void exgcd(LL a,LL b)
{
    if (!b){
        x = 1;y = 0;
        return;
    }
    LL d = exgcd(b, a%b, y, x);
    y -= a/b*x;
    return d;
}
int main()
{
    cin >> a >> b >> c >> x1 >> x2 >> y1 >> y2;
    c=-c;
    if (c<0) {
        a = -a, b =- b, c =- c;
    if (a<0) {
        a =- a; 
        LL t = x1;
        x1 = -x2; x2 = -t;
    }
    if (b<0) {
        b = -b;
        LL t = y1; 
        y1 = -y2; y2 = -t;
    }
    if (a == 0 && b == 0)
    {
        if (c == 0)
        {
            cout << (x2-x1+1)*(y2-y1+1) <<endl;
            return 0;
        }else 
            cout <<0<<endl; 
        return 0;
    }
    else if (a == 0)
    {
        if (c%b == 0)
            if (c/b<=y2 && c/b>=yy1)  cout << x2-x1+1 <<endl;
        else cout << 0 << endl;
        return 0;
    }
    else if (b == 0)
    {
        if (c%a == 0)
            if (c/a <= x2 && c/a >= x1) cout << y2-y1+1 << endl;
        else printf("0");return 0;
    }

    long long d=gcd(a,b);
    if (c%d!=0){printf("0");return 0;}

    a=a/d;b=b/d;c=c/d;
    exgcd(a,b);
    x0=x0*c;yy0=yy0*c;

    double tx2=x2,tx1=x1,tx0=x0,ta=a,tb=b,tc=c,ty1=yy1,ty2=y2,ty0=yy0;
    long long down1=floor(((tx2-tx0)/tb)),down2=floor(((ty0-ty1)/ta));
    long long r=cmin(down1,down2);
    long long up1=ceil(((tx1-tx0)/tb)),up2=ceil(((ty0-ty2)/ta));
    long long l=cmax(up1,up2);
    if (r<l) printf("0");
    else printf("%I64d",r-l+1);
    return 0;

}

25.Farey Sequence

1.题意

欧拉函数模板题

2.思路

素数筛+欧拉函数+前缀和

3.代码
#include "iostream"
using namespace std;
typedef long long LL;
const int N = 1e6+10;
int prime[N],cnt;
LL phi[N];
bool st[N];
void eluer(int n){
    phi[1] = 1;
    for(int i = 2; i <= n; ++i){
        if(!st[i])  {
            prime[cnt ++] = i;
            phi[i] = i-1;
        }
        for(int j = 0; prime[j] <= n/i; ++j){
            st[prime[j] * i] = 1;
            if(i % prime[j] == 0) {
                phi[prime[j] * i] = prime[j]*phi[i];
                break;
            }
            else phi[prime[j] * i] = (prime[j]-1)*phi[i];
        }
    }
}
int main(){
    int n;
    eluer(N);
    for(int i = 3; i <= N; ++i) phi[i]+=phi[i-1];
    while(cin>>n,n){
        cout << phi[n] << endl;
    }
    return 0;
}

26.The Super Powers

1.题意

输出所有可以表示成a^b 或者(a^ b)^c的数
比如:
16 = 2^4= 4^2
64 = 8^2 = 2^6

2.思路

最大到2^64-1暴力找肯定超时

可以想到, 如果a^b可以拆分, 那么b一定是个合数
如此看来幂的范围是:[4 - 64)
底数范围:[2, 2^16)
a的最大次幂:b = loga(2^64-1) = log(2^64-1) / log(i);
求的过程中会有重复的,用set存即可去重

3.代码
#include "iostream"
#include "set"
#include "cmath" 
using namespace std;
typedef unsigned long long LL;
const int N = 1<<16;
set<LL>se;
const int index[50] =
{
	4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 22, 24, 25, 26,
	27, 28, 30, 32, 33, 34, 35, 36, 38, 39, 40, 42, 44, 45, 46,
	48, 49, 50, 51, 52, 54, 55, 56, 57, 58, 60, 62, 63, 64  /// 多算一个
};///合数表
int main(){
    se.insert(1);
    LL x=1;
    for(int i = 2; i < N; ++i){
        x=(LL)i*i;
        x*=x;
        se.insert(x);
		int t = ceil(64 * log(2) / log(i)) - 1;
        for(int j = 1; index[j] <= t; ++j){
            x*=(index[j]-index[j-1]==2)?i*i:i;
            se.insert(x);
        }
    }
    for(auto s : se)
        cout << s<<endl;
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值