动态规划

思路

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 2010,M = 1e9 + 7;

int C[N][N];

int n;
int a,b;

int main() {
for(int i = 0; i < N; ++ i)
for(int j = 0; j <= i; ++ j)
if(!j) C[i][j] = 1;
else C[i][j] = (C[i-1][j] + C[i-1][j-1]) % M;
cin >> n;
while(n--) {
cin >> a >> b;
cout << C[a][b] << endl;
}
return 0;
}

乘法逆元

乘法逆元

如果 $b$ 与 $p$ 互质,且对于任意整数 $a$,如果 $b|a$,则存在一个整数 $x$,$\frac{a}{b} \equiv {a \times x} \pmod p$, $x$ 称为 $b$ 的模 $p$ 逆元。

$b$ 存在乘法逆元的充要条件是 $b$ 与模数 $p$ 互质。当模数 $p$ 为质数时,$b^{p−2}$ 即为 $b$ 的乘法逆元。

思路

预处理出所有数据范围内的阶乘及其逆元,根据$C_a^b=\frac{a!}{b! \times (a - b)!}$。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 1e5 + 10,M = 1e9 + 7;

int n,a,b;

int fact[N],infact[N];

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

int main() {
fact[0] = infact[0] = 1;
for(int i = 1; i < N; ++ i){
fact[i] = (LL)fact[i-1] * i % M;
infact[i] = (LL)infact[i-1] * qmi(i,M - 2,M) % M;
}
scanf("%d", &n);
while(n--) {
scanf("%d%d",&a,&b);
int res = (LL)fact[a] * infact[b] % M * infact[a-b] % M;
printf("%d\n",res);
}
return 0;
}

Lucas定理

思路

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 1e5 + 10;

int n;
LL a,b;
int p;

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

int C(LL a,LL b,int p) {
int res = 1 % p;
for(int i = 1, j = a; i <= b; ++ i, -- j) {
res = (LL)res * j % p;
res = (LL)res * qmi(i,p - 2,p) % p;
}
return res;
}

int lucas(LL a,LL b,int p) {
if(a < p) return C(a,b,p);
int res = 1;
res = (LL)C(a % p,b % p, p) * lucas(a / p,b / p,p) % p;
return res;
}

int main()
{
scanf("%d", &n);
while(n--) {
scanf("%lld%lld%d",&a,&b,&p);
printf("%d\n",lucas(a,b,p));
}
return 0;
}

高精度

思路

根据算术基本定理,把三个数分解成若干质数乘积,然后约分,最后只剩高精度乘法运算。

如何求 $a$ 中包含多少个 $p$?

算出一个$p$的个数,然后是$p^2$的个数,$p^2$包含两个$p$,但是由于$p^2$中的一个$p$已经加上了,故只需加一次,以此类推。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <unordered_map>

using namespace std;

const int N = 5010;

int a,b;
int primes[N];
bool st[N];
int cnt;
int sum[N];

vector<int> mul(vector<int> &a,int b) {
vector<int> c;
int t = 0;
for(int i = 0; i < a.size(); ++ i) {
t += a[i] * b;
c.push_back(t % 10);
t /= 10;
}
while(t) {
c.push_back(t % 10);
t /= 10;
}
return c;
}

void get_primes(int n) {
for(int i = 2; i <= n; ++ i) {
if(!st[i]) primes[cnt ++] = i;
for(int j = 0; primes[j] <= n / i; ++ j) {
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}

int get(int x,int p) {
int res = 0;
while(x) {
res += x / p;
x /= p;
}
return res;
}

int main() {
cin >> a >> b;
get_primes(a);
for(int i = 0; i < cnt; ++ i)
sum[i] = get(a,primes[i]) - get(b,primes[i]) - get(a - b,primes[i]);
vector<int> res;
res.push_back(1);
for(int i = 0 ; i < cnt; ++ i)
for(int j = 0; j < sum[i]; ++ j)
res = mul(res,primes[i]);
for(int i = res.size() - 1; i >= 0; -- i) printf("%d",res[i]);
return 0;
}