kuangbin带你飞专题十四数论基础

数论知识

在这里插入图片描述
图片转自

LightOJ 1370 Bi-shoe and Phi-shoe

题意

找出比自己大且欧拉函数也比自己大的第一个数

思路

很巧妙, 找比自己大的第一个质数就可以, 因为质数x, ϕ ( x ) = x − 1 \phi(x) = x-1 ϕ(x)=x1

代码

#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 1e6 + 5;
int prime[maxn];

void doprime(){
    memset(prime,0,sizeof(prime));
    prime[1]=1;
    for(int i=2;i*i<maxn;i++){
        if(!prime[i]){
            for(int j=2*i;j<maxn;j+=i){
                prime[j]=1;
            }
        }
    }
}

int solve(int n){
    for(int i=n+1;;i++){
        if(!prime[i])
            return i;
    }
}

int main(){
    int t,n,num,cas=1;
    doprime();
    cin>>t;
    while(t--){
        long long ans = 0;
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>num;
            ans += solve(num);
        }
        ///Case 1:22
        cout<<"Case "<<cas++<<": "<<ans<<" Xukha"<<endl;
    }
}

LightOJ 1341 Aladdin and the Flying Carpet

题意

找出所有的约数对数, 并且满足对内的元素不小于b

思路

步骤:

  1. 筛质数
  2. 分解质因数,求约数个数
  3. 因为求对数ans,有重复,除以ans / 2,
  4. dfs筛所有约数
  5. 减去小于b的情况的约数
    注意: 筛所有约数记得排序

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#define dbg(a) cout<<#a<<" : "<<a<<endl
using namespace std;

typedef long long LL;
typedef pair<LL, int> PII;
const int N = 1000010;

int primes[N];
int cnt;
int cntf, cntd;
LL divisor[N];
PII factor[N];
bool st[N];

// 筛质数
void init() {
    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(LL u, LL p){
    if(u > cntf) {
        divisor[cntd ++] = p;
        return;
    }

    for(int i = 0; i <= factor[u].second; i ++) {
        dfs(u + 1, p);
        p = (LL) p * factor[u].first;
    }
}


int main(){
    init();

    int T;
    cin >> T;
    for(int k = 1; k <= T; k ++) {
        cntd = cntf = 0;
        LL a, b;
        cin >> a >> b;
        if(a< b * b) {
			printf("Case %d: 0\n", k);
			continue;
		}
        printf("Case %d: ", k);
        LL sum = 1;
        // 质因子分解
        for(int i = 0; primes[i] <= a / primes[i]; i ++) {
            LL p = primes[i];
            if(a % p == 0) {
                LL s = 0;
                while(a % p == 0) {
                    a /= p;
                    s ++;
                }
                factor[++ cntf] = {p, s};
                sum = (LL)sum * (s + 1);
            }
        }
        if(a > 1) {
            factor[++ cntf] = {a, 1};
            sum = (LL)sum *(1 + 1);
        }

        sum /= 2;
		
		//筛约数
        dfs(1, 1);

        sort(divisor, divisor + cntd);
        for(int i = 0; i < cntd; i ++) {
            if(b <= divisor[i]) break;
            sum --;
        }

        printf("%lld\n", sum);
        }

    return 0;

}

LightOJ - 1282 Leading and Trailing

思路

太妙了,简直就是妙蛙种子吃着妙脆角妙进了米奇妙妙屋.
看大佬博客的。

  1. 取后三位,快速幂取模就行。LL trail = qmi(n, k, 1000);
  2. 取前三位就太喵了。
    n k = 1 o l g n k = 1 0 k ∗ l g n n^k = 1o^{lgn^k} = 10^{k*lgn} nk=1olgnk=10klgn
    k ∗ l g n = a + b k*lg^n = a + b klgn=a+b,a是值 k ∗ l g n k*lg^n klgn 的整数部分, b是小数部分, 则 n k = 1 0 a ∗ 1 0 b n^k = 10^a * 10^b nk=10a10b
    可以看出 1 0 a 10^a 10a不会改变 n k n^k nk的大小,所以前三位取决于 1 0 b 10^b 10b
    所以只需求b出来即可,怎么求呢?
    用到个库函数可以求浮点数的小数部分,设为y。modf()
    所以前三位为: pow(10, y) * 100;
    若不足,输出语句添上补0即可

代码

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

using namespace std;

typedef long long LL;

LL qmi(LL a, LL b, LL p) {
    LL res = 1;
    while(b) {
        if(b & 1) res = (LL)res * a % p;
        a = (LL) a * a % p;
        b >>= 1;
    }
    return res;
}

int main() {
    int T;
    cin >> T;
    for(int i = 1; i <= T; i ++) {
        LL n, k;
        cin >> n >> k;
        LL trail = qmi(n, k, 1000);
        double x, y;
        y = modf((double) k * log10(n), &x);
        LL lead = pow(10, y) * 100;
        printf("Case %d: %03lld %03lld\n",i, lead,trail);
    }

    return 0;
}

LightOJ - 1220 Mysterious Bacteria

思路

又是质因子分解咯
N = p 1 c 1 ∗ p 2 c 2 ∗ . . . ∗ p k c k N = p_1^{c1} * p_2^{c2} * ...* p_k^{c_k} N=p1c1p2c2...pkck
要想化成 b a b^a ba ,则 N = ( p 1 c 1 / a ∗ p 2 c 2 / a ∗ . . . ∗ p k c k / a ) a N = (p_1^{c1/a} * p_2^{c2/a} * ...* p_k^{c_k/a})^a N=(p1c1/ap2c2/a...pkck/a)a
则求 a = g c d ( c 1 , c 2 , c 3 , . . . , c k ) a = gcd(c1, c2, c3, ..., ck) a=gcd(c1,c2,c3,...,ck)即可
注意当是负数的时候要特殊处理, 则幂 a a a 不能是偶数

代码

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

using namespace std;

const int N = 100010;

int primes[N];
bool st[N];
int cnt;

void init() {
    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;
        }
    }
}

int gcd(int a, int b){
    return b?gcd(b, a % b):a;
}
int main() {
    init();

    int T;
    scanf("%d", &T);
    for(int k = 1; k <= T; k ++) {
        int n;
        int f = 0;
        scanf("%d", &n);
        if(n < 0) {n = -n, f = 1;}
        int d = 0;
        for(int i = 0; i < cnt; i ++) { // primes[i] < n / primes[i]也可
            if(n % primes[i] == 0) {
                int s = 0;
                while(n % primes[i] == 0) {s ++, n /= primes[i];}
                if(d == 0) d = s;
                else d = gcd(d, s);
            }
        }

        if(n > 1) d = gcd(d, 1);

        if(f) {
            while(d % 2 == 0) d >>= 1;
        }

        printf("Case %d: %d\n",k, d);
    }

    return 0;

}

LightOJ - 1234 Harmonic Number

思路

结论题咯。
N较小,暴力求解。
N较大, a n s = l o g ( n ) + C + 1.0 / ( 2 ∗ n ) , ( C = 0.57721566490153286060651209 ) 欧 拉 常 数 ans = log(n) + C + 1.0/(2 * n), (C = 0.57721566490153286060651209) 欧拉常数 ans=log(n)+C+1.0/(2n),(C=0.57721566490153286060651209)

代码

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

using namespace std;

const int N = 100010;
const double r = 0.57721566490153286060651209;
double s[N];

int main() {
    for(int i = 1; i < N; i ++) {
        s[i] = s[i - 1] + 1.0 / i;
    }
    int T;
    scanf("%d", &T);
    for(int t = 1; t <= T; t ++) {
        int n;
        scanf("%d", &n);
        if(n < N) printf("Case %d: %.10f\n",t, s[n]);
        else printf("Case %d: %.10f\n", t, log(n) + r + 1.0/(2 * n));
    }

    return 0;
}

LightOJ - 1214 Large Division

思路

大数除法

代码

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

using namespace std;
typedef long long LL;

int main(){
    int T;
    scanf("%d", &T);
    for(int k = 1; k <= T; k ++) {
        vector<int> A;
        string str;
        cin >> str;
        LL b;
        cin >> b;
        if(b < 0) b = -b;
        for(int i = 0; i < str.size(); i ++) {
            if(str[i] == '-') continue;
            else A.push_back(str[i] - '0');
        }
        LL t = 0;
        for(int i = 0; i < A.size(); i ++) {
            t = t * 10 + A[i];
            t %= b;
        }

        if(t == 0) printf("Case %d: divisible\n", k);
        else printf("Case %d: not divisible\n", k);
    }

    return 0;
}

import java.math.BigInteger;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int t = s.nextInt();
        int cas=1;
        while(t-->0)
        {
            BigInteger a = s.nextBigInteger();
            BigInteger b = s.nextBigInteger();
            b=b.abs();
            if(a.mod(b)==BigInteger.ZERO)
            {
                System.out.println("Case "+cas+": "+"divisible");
                cas++;
            }
            else
            {
                System.out.println("Case "+cas+": "+"not divisible");
                cas++;
            }
        }
    }
}

LightOJ - 1197Help Hanzo

思路

二次筛法咯。
线性筛 + 埃氏筛法

代码

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

using namespace std;

const int N = 1000010;
int primes[N];
int cnt;
bool st[N];

typedef long long LL;

void init(int n) {
    memset(st, 0, sizeof st);
    cnt = 0;
    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;
        }
    }
}
int main() {
    int T;
    init(50000);
    scanf("%d", &T);
    for(int k = 1; k <= T; k ++) {
        int l, r;
        scanf("%d%d", &l, &r);
        memset(st, false, sizeof st);
        for(int i = 0; i < cnt; i ++) {
            LL p = primes[i];
            // 二次筛,筛区间质数, 这里要p*2, 防止结果l等于p,把p也给筛咯
            for(LL j = max((l + p - 1) / p * p, p * 2); j <= r; j += p)
            	//偏移量, 若太大存不住
                st[j - l] = true;
        }
        cnt = 0;
        for(int i = 0; i <= r - l; i ++) {
        	// 把1情况去掉
            if(!st[i] && i + l >= 2)
                cnt ++;
        }
        printf("Case %d: %d\n", k, cnt);
    }

    return 0;
}

LightOJ - 1138 Trailing Zeroes (III)

思路

N!中分解质因子 其中的 5 k 5^k 5k, 质因子5的幂k有多大, 则后面就有多少个0
类似阶乘分解的题。
N ! N! N!, 计算其中质因子5的幂。
利用 下面代码计算N!中, p的幂s

for(LL i = n; i; i /= p) {
        s += i / p;
    }

然后利用二分来枚举最优解咯

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

using namespace std;

typedef long long LL;

LL num;

LL solve(LL n) {
   LL s = 0;
   for(LL i = n; i; i /= 5) {
       s += i / 5;
   }

   return s;
}

int main() {
   int T;
   scanf("%d", &T);
   for(int t = 1; t <= T; t ++) {
       scanf("%lld", &num);
       LL l = 1, r = 1e12;
       while(l < r) {
           int mid = l + r >> 1;
           if(solve(mid) >= num) r = mid;
           else l = mid + 1;
       }

       if(solve(l) == num)printf("Case %d: %lld\n", t, l);
       else printf("Case %d: impossible\n", t);
   }

   return 0;
}

POJ - 2115 C Looooops

题意

求最小x满足, a x + b ≡ c   ( m o d   2 k ) ax + b \equiv c \ (mod \ 2^k) ax+bc (mod 2k)

思路

a + c x = b + y ∗ 2 k a + cx = b + y*2^k a+cx=b+y2k
c x − 2 k ∗ y = b − a cx - 2^k*y = b - a cx2ky=ba
c x + 2 k ∗ y ′ = b − a cx + 2^k*y' = b - a cx+2ky=ba
用扩展欧几里得算法求即可
a x + b y = g c d ( a , b ) ax + by = gcd(a,b) ax+by=gcd(a,b)通解:

{ x = x 0 + b g c d ( a , b ) ∗ t y = y 0 − a g c d ( a , b ) ∗ t \left\{ \begin{aligned} & x = x_0 + \frac{b}{gcd(a, b) } * t \\ & y = y_0 - \frac{a}{gcd(a, b)} * t \end{aligned} \right. x=x0+gcd(a,b)bty=y0gcd(a,b)at

代码

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

using namespace std;

typedef long long LL;

LL exgcd(LL a, LL b, LL &x, LL &y) {
    if(!b) {
        x = 1, y = 0;
        return a;
    }
    LL x1, y1;
    LL d = exgcd(b, a % b, x1, y1);
    x = y1;
    y = x1 - a / b * y1;
    return d;

}
int main() {
    LL a, b, c, k;
    while(~scanf("%lld%lld%lld%lld", &a, &b, &c, &k) && a + b + c + k) {
        LL z = (LL)1 << k;
        LL x, y;
        LL d = exgcd(c, z, x, y);
        LL p = b - a;
        if(p % d != 0) puts("FOREVER");
        else {
            x = (LL)x * (p / d);
            LL t = z / d;
            x = (x % t + t) % t;
            printf("%lld\n", x);
        }
    }

    return 0;
}

SGU - 106 The equation

思路

扩展欧几里得+区间交集+细节处理
代码转自:xzx9

代码

    #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x1,x2,y1,y2;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1;
        y=0;
        return a;
    }
    ll ans=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-(a/b)*y;
    return ans;
}
ll cal(ll a,ll b,ll c)
{
    ll x,y;
    ll gcd=exgcd(a,b,x,y);
    if(c%gcd)
        return 0;
    x=x/gcd*c;
    y=y/gcd*c;

    int t1 = b / gcd;
    int t2 = a / gcd;
    //左边界上取整,右边界下取整
    ll r1=floor(1.0*(x2-x)/t1);
    ll l2=ceil(1.0*(y-y2)/t2);
    ll l1=ceil(1.0*(x1-x)/t1);
    ll r2=floor(1.0*(y-y1)/t2);
    //区间内交集
    ll l=max(l1,l2);
    ll r=min(r1,r2);
    return l<=r?r-l+1:0;
}
int main()
{
    ll a,b,c,ans=0; 
    scanf("%lld%lld%lld",&a,&b,&c);
    scanf("%lld%lld",&x1,&x2);
    scanf("%lld%lld",&y1,&y2);
    if(c>0)//预处理方便处理
        c=-c,a=-a,b=-b;
    if(a<0)
        a=-a,swap(x1,x2),x1=-x1,x2=-x2;
    if(b<0)
        b=-b,swap(y1,y2),y1=-y1,y2=-y2;
    if(x1>x2||y1>y2)//分类讨论
        ans=0;
    if(a==0&&b==0)
    {
        if(c==0)
            ans=(x2-x1+1)*(y2-y1+1);
    }
    else if(a==0)
    {
        ll t=-c/b;
        if(b*t+c==0&&t>=y1&&t<=y2)
           ans=x2-x1+1;
    }
    else if(b==0)
    {
        ll t=-c/a;
        if(a*t+c==0&&t>=x1&&t<=x2)
            ans=y2-y1+1;
    }
    else
        ans=cal(a,b,-c);
    printf("%lld\n",ans);
    return 0;
}

UVA - 10200 Prime Time

思路

预处理前缀和咯
注意:

  1. 精度丢失问题,末尾+1e-8
  2. L可能为0, 直接取sum[r] 即可

代码

#include<iostream>
using namespace std;

const int N = 1e5 + 10;
int sum[N];

bool is_prime(int x) {
    for(int i = 2; i * i <= x; i ++)
        if(x % i == 0) return false;
    return true;
}

void init() {
    for(int i = 0; i <= 10010; i ++) {
        if(!i) {sum[i] = 1; continue;}
        if(is_prime(i * i + i + 41)) {
            sum[i] = sum[i - 1] + 1;
        }
        else sum[i] = sum[i - 1];
    }
}

int main() {
    init();
    int l, r;
    while(~scanf("%d%d", &l, &r)) {
        double ans;
        if(l == 0) {
            ans = 100 * (sum[r]) * 1.0 / (r + 1);
        }
        else {
            ans = 100 * (sum[r] - sum[l - 1]) * 1.0 / (r - l + 1);
        }

        printf("%.2f\n", (double) ans + 1e-8);
    }

    return 0;
}

POJ - 2478 Farey Sequence

思路

看一会就知道规律了咯
就是筛欧拉函数模板题咯

代码

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

using namespace std;
typedef long long LL ;
const int N = 1000010;

int primes[N];
bool st[N];
int cnt;
LL sum[N];
LL phi[N];

typedef long long LL;

void init() {
    phi[1] = 1;
    for(int i = 2; i < N; i ++) {
        if(!st[i]) {
            primes[cnt ++] = i;
            phi[i] = i - 1;
        }
        for(int j = 0; (LL)primes[j] * i < N; j ++) {
            int t = primes[j] * i;
            st[t] = true;
            if(i % primes[j] == 0) {
                phi[t] = phi[i] * primes[j];
                break;
            }
            phi[t] = phi[i] * (primes[j] - 1);
        }
    }   

    for(int i = 1; i < N; i ++)
        sum[i] = sum[i - 1] + phi[i];
}

int main() {
    int n;
    init();
    while(~scanf("%d", &n) && n) {
        printf("%lld\n", sum[n] - 1);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值