BUPT2017 springtraining(16) #4 ——基础数论

题目在这里

A.手动打表找规律得组合数

n -= 2, m -= 2, ans = C(n, m)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int Mod = 1e9 + 7;

ll fac[200010];

ll calc(ll x, int k = Mod - 2) {
    ll ret = 1;
    for(;k;k >>= 1, x = x * x % Mod)
        if(k & 1) ret = ret * x % Mod;
    return ret;
}

int main() {
    ios::sync_with_stdio(false);
    int n, m;
    fac[0] = fac[1] = 1;
    for(int i = 2;i <= 200000;i ++) 
        fac[i] = fac[i - 1] * i % Mod;
    while(cin >> n >> m) {
        if(n > m) swap(n, m);
        cout << fac[m + n - 4] * calc(fac[n - 2]) % Mod * calc(fac[m - 2]) % Mod << endl;
    }
    return 0;
}
View Code

B.

C.裸快速幂

#include <cstdio>

using namespace std;

double x = 1.000000011;

double n;

long long k;

int main() {
    scanf("%lf %lld", &n, &k);
    for(;k;k >>= 1, x = x * x)
        if(k & 1) n = n * x;
    printf("%.8f
", n);
    return 0;
}
View Code

D.递推式那么明显当然是矩阵快速幂啦

唯一需要注意的是,自乘矩阵的填充方式...

开始时,a1 对应 f(d) , a2 对应 f(d - 1) ...

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int d, n, m;

struct  matrix1{
    ll c[15][15];
    matrix1() {
        memset(c, 0, sizeof c);
    }
    void init() {
        memset(c, 0, sizeof c);
        for(int i = 0;i < d;i ++) cin >> c[d - 1 - i][d - 1], c[d - 1 - i][d - 1] %= m;
        for(int i = 1;i < d;i ++) c[i][i - 1] = 1;
    }
    matrix1 operator *(const matrix1 &a) const {
        matrix1 ret;
        for(int k = 0;k < d;k ++)
            for(int i = 0;i < d;i ++)
                if(c[i][k])
                    for(int j = 0;j < d;j ++)
                        ret.c[i][j] = (ret.c[i][j] + c[i][k] * a.c[k][j]) % m;
        return ret;
    }
};

struct  matrix2{
    ll c[15];
    matrix2() {
        memset(c, 0, sizeof c);
    }
    void init() {
        memset(c, 0, sizeof c);
        for(int i = 0;i < d;i ++) cin >> c[i], c[i] %= m;
    }
    matrix2 operator *(const matrix1 &a) const {
        matrix2 ret;
        for(int i = 0;i < d;i ++)
            for(int j = 0;j < d;j ++)
                ret.c[i] = (ret.c[i] + c[j] * a.c[j][i]) % m;
        return ret;
    }
};

int main() {
    matrix1 c1;
    matrix2 c2;
    while(cin >> d >> n >> m, d != 0) {
        c1.init(), c2.init();
        for(n --;n;n >>= 1, c1 = c1 * c1)
            if(n & 1) c2 = c2 * c1;
        cout << c2.c[0] << endl;
    } 
    return 0;
}
View Code

E.参考 CodeForces - 785D 

F.看样例猜结论就好了

输出所有质数的幂就好了

#include <iostream>

using std::cin;
using std::endl;
using std::cout;

int n, v[1011], p[1011];

int main() {
    cin >> n;
    for(int i = 2;i <= n;i ++) {
        if(!v[i]) {
            for(int j = i;j <= n;j *= i)
                p[++ p[0]] = j;
        }
        for(int j = i << 1;j <= n;j += i)
            v[j] = 1;
    }
    cout << p[0] << endl;
    for(int i = 1;i <= p[0];i ++)
        cout << p[i] << " ";
    return 0;
}
View Code

G.参考 CodeForces - 785C

H.

I.能被 6 整除一定能被 2 和 3 整除,8 和 9 同理

于是就是求 1 - n 内2 3 5 7的倍数之和

容斥原理即可

#include <iostream>

using namespace std;

int main() {
    long long n;
    cin >> n;
    cout << n - n / 2 - n / 3 - n / 5 - n / 7 + n / 6 + n / 10 + n / 14 + n / 35 + n / 21 + n / 15 - n / 105 - n / 70 - n / 42 - n / 30 + n / 210;
    return 0;
}
View Code

J.

K.对于n 的每一个因数 t 

它对答案贡献为 t * phi(n / t)

效率玄学吧

#include <cstdio>

int v[60000], p[60000];

int phi(int x) {
    int ret = x;
    for(int i = 1;1ll * p[i] * p[i] <= x;i ++)
        if(x % p[i] == 0) {
            ret = ret - ret / p[i];
            while(x % p[i] == 0) x /= p[i];
        }
    if(x != 1) ret -= ret / x;
    return ret;
}

int main() {
    long long n, ans;
    for(int i = 2;i < 60000;i ++) {
        if(!v[i]) p[++ p[0]] = i;
        for(int j = 1;j <= p[0] && p[j] * i < 60000;j ++) {
            v[p[j] * i] = 1;
            if(i % p[j] == 0) break;
        }
    }
    while(scanf("%lld", &n) != EOF) {
        ans = 0;
        for(long long i = 1;i * i <= n;i ++) {
            if(n % i)  continue;
            ans += i * phi(n / i);
            if(i * i != n) ans += n / i * phi(i);
        }
        printf("%lld
", ans);
    }
    return 0;
}
View Code

L.和式加一项减一项变成了C(n + m, n)

n,m 大 p 小且 p 保证为质数,使用卢卡斯定理

Lucas(n,m) % p = Lucas(n / p,m / p) * Comb(n % p,m % p) % p

注意lucas里面的取模后计算组合数,可能会出现 C(n,m) 里 n < m

所以Comb函数里需要判断的

考虑 p 为读入的不能预处理,所以我们有两种解决方案

1.每组数据 O(p) 预处理处阶乘 fac 

Comb就可以 O(logn) 回答,理论效率 O(p + logn * log(p)(n + m))

大概就是 O(p)

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int t, n, m, p;

ll fac[100010];

ll calc(ll x, int k = p - 2) {
    ll ret = 1;
    for(;k;k >>= 1, x = x * x % p)
        if(k & 1) ret = ret * x % p;
    return ret;
} 

ll C(int n, int m) {
    if(n < m) return 0;
    return fac[n] * calc(fac[m]) % p * calc(fac[n - m]) % p;
}

ll lucas(int n, int m) {
    if(!m) return 1;
    return C(n % p, m % p) * lucas(n / p, m / p) % p;
}

int main() {
    ios::sync_with_stdio(false);
    fac[0] = 1;
    cin >> t;
    while(t --) {
        cin >> n >> m >> p;
        for(int i = 1;i < p;i ++) fac[i] = fac[i - 1] * i % p;
        cout << lucas(n + m, n) << endl;
    }
    return 0;
}
View Code

2.不进行预处理,Comb直接 O(p + logn) 回答 

理论效率O(p * log(p)(n + m))

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

int t, n, m, p;

ll calc(ll x, int k = p - 2) {
    ll ret = 1;
    for(;k;k >>= 1, x = x * x % p)
        if(k & 1) ret = ret * x % p;
    return ret;
} 

ll C(int n, int m) {
    if(n < m) return 0;
    ll ret1 = 1, ret2 = 1;
    for(int i = 1, j = n;i <= m;i ++, j --) {
        ret1 = ret1 * i % p;
        ret2 = ret2 * j % p;
    }
    return ret2 * calc(ret1) % p;
}

ll lucas(int n, int m) {
    if(!m) return 1;
    return C(n % p, m % p) * lucas(n / p, m / p) % p;
}

int main() {
    ios::sync_with_stdio(false);
    cin >> t;
    while(t --) {
        cin >> n >> m >> p;
        cout << lucas(n + m, n) << endl;
    }
    return 0;
}
View Code

然而第二种的表现更好...因为都是最坏时间估计

而第一种稳定在O(p),第二种实际是几倍优于最坏效率的

M.

原文地址:https://www.cnblogs.com/ytytzzz/p/6929504.html