2 ~ n-1
没有任何一个数字可以整除 n。2 ~ n-1
,只需要判断 2 → n 2 \rightarrow \sqrt{n} 2→n 即可。(代码有个小优化,我们判断是否到 n \sqrt{n} n 不用 sqrt 函数(可能有精度问题),也不用 i * i <= x
可能有整数溢出问题,而是用 i < = x / i
)#include
using namespace std;
bool is_prime(int x) {
for (int i = 2; i <= x / i; ++ i ) {
if (x % i == 0) return false;
}
return true;
}
int main() {
int n;
cin >> n;
while (n --) {
int x;
cin >> x;
if (is_prime(x)) puts("Yes");
else puts("No");
}
return 0;
}
2 ~ n
的所有质数,然后试除法,如果 n % x == 0
说明该数是一个质因数,就可以求得其指数。n = n / i;
直到 n 不能整除当前数,就得到了答案。
#include
using namespace std;
void get_prime(int x) {
for (int i = 2; i <= x / i; ++ i) {
if (x % i == 0) {
int s = 0;
while (x % i == 0) x /= i, s ++;
cout << i << " " << s << endl;
}
}
if (x > 1) cout << x << " 1" << endl;
return;
}
int main() {
int n;
while (n --) {
int x;
cin >> x;
get_prime(x);
}
return 0;
}
筛出 1 - n 的所有质数:
#include
using namespace std;
const int N = 1e6 + 10;
int primes[N], cnt;
bool st[N];
// 朴素筛法
void get_prime_0(int n) {
for (int i = 2; i <= n; ++ i ) {
if (!st[i]) primes[cnt ++] = i;
for (int j = i + i; j <= n; j += i) st[j] = true;
}
}
// 埃氏筛法 O(nloglogn)
void get_prime_1(int n) {
for (int i = 2; i <= n; ++ i ) {
if (!st[i]) {
primes[cnt ++] = i;
for (int j = i + i; j <= n; j += i)
st[j] = true;
}
}
}
// 线性筛法
void get_prime(int n) {
for (int i = 2; i <= n; ++ i ) {
if (!st[i]) primes[cnt ++] = i;
for (int j = 0; primes[j] <= n / i; ++ j ) {
primes[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
int main() {
int n;
cin >> n;
get_prime(n);
cout << cnt << endl;
return 0;
}
#include
#include
#include
using namespace std;
void get_divisors(int x) {
vector<int> res;
for (int i = 1; i <= x / i; ++ i ) {
if (x % i == 0) {
res.push_back(i);
if (i != x / i) res.push_back(x / i);
}
}
sort(res.begin(), res.end());
for (auto num : res) cout << num << " ";
puts("");
return;
}
int main() {
int n;
cin >> n;
while (n --) {
int x;
cin >> x;
get_divisors(x);
}
return 0;
}
由上面得到的结论: N = p 1 a 1 × p 2 a 2 × p 3 a 3 × . . . × p k a k ( p i 是 质 数 ) N = p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3} \times ... \times p_k^{a_k} (p_i 是质数) N=p1a1×p2a2×p3a3×...×pkak(pi是质数)
每个数的约数,可以由所有这些质数相乘,每个质数的次数可以在 [ 0 , k i ] [0, k_i] [0,ki] 之间任意选择,所以所有的约数的个数就是: ∏ i = 1 k a i + 1 \prod_{i =1}^{k} a_i + 1 ∏i=1kai+1 。
#include
#include
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
unordered_map<int, int> p;
int main() {
int n;
while(n --) {
int a;
cin >> a;
for (int i = 2; i <= a / i; ++ i ) {
while (a % i == 0) {
a /= i;
p[i] ++;
}
}
if (a > 1) p[a] ++;
}
LL res = 1;
for (auto num : p)
res = res * (nums.second + 1) % mod;
cout << res << endl;
return 0;
}
我们知道所有约数的个数是由所有质数的组合,那么把每一种质数的组合加到一起就是约数之和,加到一起这个式子可以简化为: ∏ i = 1 k p i 0 + p i 1 + . . . + p i a i \prod_{i=1}^{k} p_i^0 + p_i^1 + ... + p_i^{a_i} ∏i=1kpi0+pi1+...+piai
想要验证展开即可。
#include
#include
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
unordered_map<int, int> p;
int main() {
int n;
cin >> n;
while (n --) {
int a;
cin >> a;
for (int i = 2; i <= a / i; ++ i ) {
while (a % i == 0) {
a /= i;
p[i] ++;
}
}
if (a > 1) p[a] ++;
}
LL res = 1;
for (auto num : p) {
LL a = p.first, b = p.second;
LL t = 1;
while (b --) t = (t * a + 1) % mod;
res = res * t % mod;
}
cout << res << endl;
return 0;
}
#include
using namespace std;
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
int n;
cin >> n;
while (n --) {
int a, b;
cin >> a >> b;
cout << gcd(a, b) << endl;
}
return 0;
}
1∼N 中与 N 互质的数的个数被称为欧拉函数,记为 ϕ(N)。
res = res / a * (a - 1)
)// 定义求欧拉函数
#include
using namespace std;
int main() {
int n;
cin >> n;
while (n -- ) {
int a;
cin >> a;
int res = a;
for (int i = 2; i <= a / i; ++ i ) {
if (a % i == 0) {
res = res / i * (i - 1);
while (a % i == 0) a /= i;
}
}
if (a > 1) res = res / a * (a - 1);
cout << res << endl;
}
return 0;
}
// 筛法求欧拉函数
#include
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
int primes[N], cnt;
int phi[N];
bool st[N];
void get_eulers(int n) {
phi[1] = 1;
for (int i = 2; i <= n; ++ i ) {
if (!st[i]) {
primes[cnt ++] = i;
phi[i] = i - 1;
}
for (int j = 0; primes[j] <= n / i; ++ j ) {
st[primes[j] * i] = true;
if (i % primes[j] == 0) {
phi[i * primes[j]] = phi[i] * primes[j];
break;
}
phi[i * primes[j]] = phi[i] * (primes[j] - 1);
}
}
LL res = 0;
for (int i = 1; i <= n; ++ i) res += phi[i];
cout << res << endl;
return;
}
int main() {
int n;
cin >> n;
get_eulers(n);
return 0;
}
若 a 与 n 互质,则 a ϕ ( n ) m o d n = 1 a^{ϕ(n)} \ mod \ {n} = 1 aϕ(n) mod n=1
证明:
假设 1-n 中和 n 互质的数为 $a_1, a_2, a_3, ;…;, a_{ϕ(n)} $,因为 a 与 n 互质,则有 :
( a × a 1 ) × ( a × a 2 ) × ( a × a 3 ) × . . . × ( a × a ϕ ( n ) ) ≡ a 1 × a 2 × a 3 × . . . × a ϕ ( n ) ( m o d n ) 即 : a ϕ ( n ) × a 1 × a 2 × a 3 × . . . × a ϕ ( n ) ≡ a 1 × a 2 × a 3 × . . . × a ϕ ( n ) ( m o d n ) (a \times a_1) \times (a \times a_2) \times (a \times a_3) \times \;...\; \times (a \times a_{ϕ(n)}) \equiv \\ a_1 \times a_2 \times a_3 \times \;...\; \times a_{ϕ(n)} \quad( mod \; n) \\ \quad 即: \\ a^{ϕ(n)} \times a_1 \times a_2 \times a_3 \times \;...\; \times a_{ϕ(n)} \equiv \\ a_1 \times a_2 \times a_3 \times \;...\; \times a_{ϕ(n)} \quad( mod \; n) (a×a1)×(a×a2)×(a×a3)×...×(a×aϕ(n))≡a1×a2×a3×...×aϕ(n)(modn)即:aϕ(n)×a1×a2×a3×...×aϕ(n)≡a1×a2×a3×...×aϕ(n)(modn)
特别的: 当 n 为质数的时候,因为 n n n 的欧拉函数为 ( n − 1 ) (n - 1) (n−1) 所以有 a n − 1 m o d n = 1 a^{n - 1} \ mod \ {n} = 1 an−1 mod n=1 (费马定理)
// 快速幂
#include
using namespace std;
typedef long long LL;
int quick_power(LL a, LL b, LL p) {
LL res = 1, t = a;
while (b) {
if (b & 1) res = res * t % p;
t = t * t % p;
b >>= 1;
}
return res % p;
}
int main() {
int n;
cin >> n;
while (n -- ) {
int a, b, p;
cin >> a >> b >> p;
cout << quick_power(a, b, p) << endl;
}
return 0;
}
// 快速幂求逆元
#include
using namespace std;
int quick_power(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = 1ll * res * a % p;
k >>= 1;
a = 1ll * a * a % p;
}
return res;
}
int main() {
int n;
cin >> n;
while (n -- ) {
int a, p;
cin >> a >> p;
if (a % p) cout << quick_power(a, p - 2, p);
else cout << "impossible" << endl;
}
return 0;
}
扩展欧几里得就是求x、y。
a 、 b a、b a、b 的 x 、 y x、y x、y 和 b 、 a m o d b b、 a\;\;mod\;\;b b、amodb 的 x ′ 、 y ′ x'、y' x′、y′ 的关系,因为 a 、 b a、b a、b 和 b 、 a m o d b b、 a\;\;mod\;\;b b、amodb 的最大公约数相同,可以看作求 b 、 a m o d b b、 a\;\;mod\;\;b b、amodb 的 x ′ 、 y ′ x'、y' x′、y′,再转化为 a 、 b a、b a、b 的 x 、 y x、y x、y。
b y + ( a m o d b ) x = d a m o d b = a − ⌊ a b ⌋ × b 则 有 : a x + b ( y − ⌊ a b ⌋ × x ) = d by \; + (a\;mod\;b)x = d \\ a \; mod \; b = a - \lfloor \frac{a}{b} \rfloor \times b \\ 则有:\\ ax + b(y- \lfloor \frac{a}{b} \rfloor \times x) = d by+(amodb)x=damodb=a−⌊ba⌋×b则有:ax+b(y−⌊ba⌋×x)=d
#include
#include
using namespace std;
int exgcd(int a, int b, int&x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int n;
scanf("%d", &n);
while (n -- ) {
int a, b;
scanf("%d %d", &a, &b);
int x, y;
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
}
return 0;
}
补充所有解的形式:
x = x 0 − b d × k y = y 0 + a d × k ( g c d ( a , b ) = d a n d k ∈ Z ) x = x_0 - \frac{b}{d} \times k \\ y = y_0 + \frac{a}{d} \times k \\ (\; gcd(a, b) = d \quad and \quad k \in Z \;) x=x0−db×ky=y0+da×k(gcd(a,b)=dandk∈Z)
所有解的形式的证明:代入 a x + b y = d ax + by = d ax+by=d
求解 a x ≡ b ( m o d m ) ax \equiv b \quad( mod \; m) ax≡b(modm) 的 x:
相当于 a x = m y + b ax = my + b ax=my+b 即 a x − m y = b ax - my = b ax−my=b ,令 y = − y y = -y y=−y,则 a x + m y = b ax + my = b ax+my=b 即扩展欧几里得。需要 b b b 必须是 g c d ( a , m ) gcd(a, m) gcd(a,m) 的整数倍,否则无解。
#include
using namespace std;
int exgcd(int a, int b, int& x, int& y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int n;
scanf("%d", &n);
while (n -- ) {
int a, b, m;
scanf("%d %d %d", &a, &b, &m);
int x, y;
int d = exgcd(a, m, x, y);
if (b % d) puts("impossible");
else printf("%d\n", 1ll * x * (b / d) % m);
}
return 0;
}
通解: x = x 0 + m g c d × k ( k ∈ Z ) x = x_0 + \frac{m}{gcd} \times k \quad (k \in Z) x=x0+gcdm×k(k∈Z)
对于得到的任意答案 x i m o d m g c d x_i \; mod \; \frac{m}{gcd} ximodgcdm 都是答案,同理 x i m o d m x_i \; mod \; m ximodm 也是答案,因为此时相当于上式 k = g c d k = gcd k=gcd 。本题最后结果对 m 取模,防止整数溢出,也得到了正确答案。
其他用途: 求解逆元,即 a x ≡ a b ( m o d p , 且 p 非 质 数 , b 和 p 互 质 ) ax \equiv \frac{a}{b} \quad(mod \;\; p,且 p 非质数, b \; 和 \; p\; 互质) ax≡ba(modp,且p非质数,b和p互质)
a b x + p b y = a abx + pby = a abx+pby=a 求解扩展欧几里得即可。
( p b y ′ + ( a b m o d p b ) pby' + (ab \;\;mod\;\; pb) pby′+(abmodpb) 依次下去, x 、 y x、y x、y 的系数都是减小的趋势。这里我感觉很中二啊,留一个标记,不知道有没有更好的解法。)
中国剩余定理要求任意两个 m ,两两互质。则有:
x = a 1 × M 1 × M 1 − 1 + a 2 × M 2 × M 2 − 1 + . . . + a k × M k × M k − 1 ( M − 1 为 M 的 逆 元 ) x = a_1\times M_1 \times M_1^{-1} + a_2\times M_2 \times M_2^{-1} + \; ... \; + a_k\times M_k \times M_k^{-1} \quad (M^{-1} 为 M 的逆元) x=a1×M1×M1−1+a2×M2×M2−1+...+ak×Mk×Mk−1(M−1为M的逆元)
对于任意两个 m 不能保证互质的,可以两两求解,最后一次求得的即为所有的解。首先根据前面的线性同余方程有如下结论:
a x + m y = b 等 价 于 : a x ≡ b ( m o d m ) ax + my = b \\ 等价于:\\ ax \equiv b \quad(mod \; m) ax+my=b等价于:ax≡b(modm)
要求解的问题如下:
x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) x ≡ a 3 ( m o d m 4 ) . . . x ≡ a k ( m o d m k ) 问 题 即 为 求 解 满 足 题 意 的 x x \equiv a_1 \quad(mod \; m_1) \\ x \equiv a_2 \quad(mod \; m_2) \\ x \equiv a_3 \quad(mod \; m_4) \\ ... \\ x \equiv a_k \quad(mod \; m_k) \\ 问题即为求解满足题意的 x x≡a1(modm1)x≡a2(modm2)x≡a3(modm4)...x≡ak(modmk)问题即为求解满足题意的x
特别的,对于两个而言有:
x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) 可 转 化 为 : x = k 1 × m 1 + a 1 x = k 2 × m 2 + a 2 则 有 : k 1 × m 1 − k 2 × m 2 = a 2 − a 1 x \equiv a_1 \quad(mod \; m_1) \\ x \equiv a_2 \quad(mod \; m_2) \\ 可转化为:\\ x = k_1 \times m_1 + a_1 \\ x = k_2 \times m_2 + a_2 \\ 则有: \\ k_1 \times m_1 - k_2 \times m_2 = a_2 - a_1 x≡a1(modm1)x≡a2(modm2)可转化为:x=k1×m1+a1x=k2×m2+a2则有:k1×m1−k2×m2=a2−a1
而上式可以用扩展欧几里得求出 k 1 、 k 2 k_1 、k_2 k1、k2 ,其通项为:
k 1 = k ′ + k × m 2 d k 2 = k ′ ′ + k × m 1 d 而 且 需 要 满 足 前 提 条 件 : ( a 2 − a 1 ) m o d g c d ( m 1 , m 2 ) = 0 k_1 = k^{'} + k \times \frac{m_2}{d} \\ k_2 = k^{''} + k \times \frac{m_1}{d} \\ 而且需要满足前提条件:\\ (a_2 - a_1) \; mod \; gcd(m_1, m_2) = 0 k1=k′+k×dm2k2=k′′+k×dm1而且需要满足前提条件:(a2−a1)modgcd(m1,m2)=0
则有:
x = k 1 × m 1 + a 1 = ( k ′ + k × m 2 d ) × m 1 + a 1 = a 1 + k ′ × m 1 + k × m 2 × m 1 d = x 0 + k × m d = k × m d + x 0 = k × m ′ + a ′ 则 x 的 最 小 正 整 数 解 为 : ( a ′ m o d m ′ + m ′ ) m o d m ′ x = k_1 \times m_1 + a_1 \\ = (k^{'} + k \times \frac{m_2}{d}) \times m_1 + a_1 \\ = a_1 + k' \times m_1 + k \times \frac{m_2 \times m_1}{d} \\ = x_0 + k \times \frac{m}{d} \\ = k \times \frac{m}{d} + x_0 \\ = k \times m' + a' \\ 则 x 的最小正整数解为:\\ (a' \; mod \; m' + m') \; mod \; m' x=k1×m1+a1=(k′+k×dm2)×m1+a1=a1+k′×m1+k×dm2×m1=x0+k×dm=k×dm+x0=k×m′+a′则x的最小正整数解为:(a′modm′+m′)modm′
当 x 仍需满足其他 x = k i × m i + a i x = k_i \times m_i + a_i x=ki×mi+ai 时,只需用上次求得的 x = k × m ′ + a ′ x = k \times m' + a' x=k×m′+a′ 重复使用扩展欧几里得即可,直到不再需要满足其他的式子,则最后输出 x x x 的最小正整数解。
#include
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 d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int n;
cin >> n;
bool has_answer = true;
LL m1, a1;
cin >> m1 >> a1;
while ( -- n ) {
LL m2, a2;
cin >> m2 >> a2;
LL k1, k2;
LL d = exgcd(m1, m2, k1, k2);
if ((a1 - a2) % d) {
has_answer = false;
break;
}
k1 *= (a2 - a1) / d;
LL t = m2 / d;
k1 = (k1 % t + t) % t; // 防止下面有整数溢出情况
a1 = a1 + k1 * m1; // 先使用 m1 避免 m1 被修改
m1 = m1 / d * m2;
}
if (has_answer) cout << (a1 % m1 + m1) % m1 << endl;
else puts("-1");
return 0;
}
#include
#include
using namespace std;
const double eps = 1e-6;
const int N = 110;
int n;
double matrix[N][N];
int gauss() {
int r = 0, c = 0;
for (; c < n; ++ c ) {
int t = r;
for (int i = r + 1; i < n; ++ i ) {
if (fabs(matrix[i][c]) > fabs(matrix[t][c])) t = i;
}
if (fabs(matrix[t][c]) < eps ) continue;
if (t != r)
for (int i = c; i < n + 1; ++ i )
swap(matrix[t][i], matrix[r][i]);
for (int i = n; i >= c; i -- ) matrix[r][i] /= matrix[r][c];
for (int i = r + 1; i < n; ++ i ) {
if (fabs(matrix[i][c]) > eps) {
for (int j = n; j >= c; j -- ) {
matrix[i][j] -= matrix[r][j] * matrix[i][c];
}
}
}
r ++;
}
if (r < n) {
for (int i = r; i < n; ++ i )
if (matrix[i][n]) return -1;
return 1;
}
for (int i = n - 1; i >= 0; i -- ) {
for (int j = i + 1; j < n; j ++ ) {
matrix[i][n] -= matrix[i][j] * matrix[j][n];
}
}
return 0;
}
int main() {
cin >> n;
for (int i = 0; i < n; ++ i ) {
for (int j = 0; j < n + 1; ++ j ) cin >> matrix[i][j];
}
int res = gauss();
if (res > 0) puts("Infinite group solutions");
else if (res < 0) puts("No solution");
else {
for (int i = 0; i < n; ++ i ) printf("%.2lf\n", matrix[i][n]);
}
return 0;
}
复习一遍高斯消元得流程
#include
using namespace std;
const int N = 110;
int n;
int a[N][N];
int gauss() {
int r, c;
for (r = c = 0; c < n; ++ c) {
int t = r;
for (int i = r + 1; i < n; ++ i ) {
if (a[i][c]) {
t = i;
break;
}
}
if (!a[t][c]) continue;
for (int i = c; i < n + 1; ++ i ) swap(a[r][i], a[t][i]);
for (int i = r + 1; i < n; ++ i ) {
if (a[i][c]) {
for (int j = c; j < n + 1; ++ j ) {
a[i][j] ^= a[r][j];
}
}
}
r ++;
}
if (r < n) {
for (int i = r; i < n; ++ i) {
if (a[i][n]) return -1;
}
return 1;
}
for (int i = n - 1; i >= 0; i -- ) {
for (int j = i + 1; j < n; ++ j ) {
a[i][n] ^= a[i][j] & a[j][n];
}
}
return 0;
}
int main() {
scanf("%d", &n);
for (int i = 0; i < n; ++ i ) {
for (int j = 0; j < n + 1; ++ j ) {
scanf("%d", &a[i][j]);
}
}
int res = gauss();
if (res < 0) puts("No solution");
else if (res > 0) puts("Multiple sets of solutions");
else {
for (int i = 0; i < n; ++ i ) cout << a[i][n] << endl;
}
return 0;
}
思想:类似 DP,可以推导 C i j = C i − 1 j + C i − 1 j − 1 C_{i}^{j} = C_{i - 1}^{j} + C_{i - 1}^{j - 1} Cij=Ci−1j+Ci−1j−1
对于 1 0 3 10^3 103 的数据,预处理一下 O ( 1 0 6 ) O(10^6) O(106),再输出是可以接受的。
#include
using namespace std;
const int N = 2010;
const int mod = 1e9 + 7;
int c[N][N];
void init() {
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 - 1] + c[i - 1][j]) % mod;
}
}
}
int read() {
int res = 0;
char ch = getchar();
while (ch >= '0' && ch <= '9') {
res = res * 10 + (ch - '0');
ch = getchar();
}
return res;
}
int main() {
init();
int n;
n = read();
while (n -- ) {
int a, b;
a = read();
b = read();
cout << c[a][b] << endl;
}
return 0;
}
求逆元: a y = a ∗ x ( m o d p ) \frac{a}{y} = a * x \quad(mod\;\; p) ya=a∗x(modp) 称 x x x 为 y y y 的 m o d p mod\;\; p modp 的逆元。当 p p p 是质数, y p − 2 y^{p-2} yp−2 就是 y y y 的一个逆元。
本题我们需要求的是阶乘的逆元,所以逆元需要累乘。
#include
using namespace std;
typedef long long LL;
const int N = 100010, mod = 1e9 + 7;
int fact[N], infact[N];
// i 的阶乘,以及 i 的阶乘的逆元
int read() {
int res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res = res * 10 + (ch - '0');
ch = getchar();
}
return res;
}
int quick_power(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = 1ll * res * a % p;
k >>= 1;
a = 1ll * a * a % p;
}
return res;
}
void init() {
fact[0] = infact[0] = 1;
for (int i = 1; i < N; ++ i ) {
fact[i] = 1ll * fact[i - 1] * i % mod;
infact[i] = 1ll * infact[i - 1] * quick_power(i, mod - 2, mod) % mod;
}
}
int main() {
init();
int n;
n = read();
while (n -- ) {
int a, b;
a = read();
b = read();
printf("%d\n", 1ll * fact[a] * infact[a - b] % mod * infact[b] % mod);
}
return 0;
}
不得不感叹卢卡斯定理的证明实在是太妙了!
这里证明不再赘述。只要展开左边观察每一项即可得证。
( 1 + x ) p ≡ ( 1 + x p ) ( m o d p ) ( 1 + x ) p a ≡ ( 1 + x p a ) ( m o d p ) (1 + x)^ p \equiv (1 + x^p) \quad(mod \;\;p) \\ (1 + x)^ {p^{a}} \equiv (1 + x^ {p^{a}}) \quad(mod \;\;p) \\ (1+x)p≡(1+xp)(modp)(1+x)pa≡(1+xpa)(modp)
首先我们可以把 a 、 b a、b a、b 转化为 p p p 进制数:
a = a k ⋅ p k + a k − 1 ⋅ p k − 1 + . . . + a 1 ⋅ p 1 + a 0 ⋅ p 0 a = a_{k}\cdot p^{k} + a_{k - 1}\cdot p^{k - 1} +\;...\;+ a_{1}\cdot p^{1} + a_{0}\cdot p^{0} a=ak⋅pk+ak−1⋅pk−1+...+a1⋅p1+a0⋅p0
b = b k ⋅ p k + b k − 1 ⋅ p k − 1 + . . . + b 1 ⋅ p 1 + b 0 ⋅ p 0 b = b_{k}\cdot p^{k} + b_{k - 1}\cdot p^{k - 1} +\;...\;+ b_{1}\cdot p^{1} + b_{0}\cdot p^{0} b=bk⋅pk+bk−1⋅pk−1+...+b1⋅p1+b0⋅p0
则有: ( 1 + x ) a = ( 1 + x ) a k ⋅ p k + a k − 1 ⋅ p k − 1 + . . . + a 1 ⋅ p 1 + a 0 ⋅ p 0 (1 + x)^a = (1 + x)^{a_{k}\cdot p^{k} + a_{k - 1}\cdot p^{k - 1} +\;...\;+ a_{1}\cdot p^{1} + a_{0}\cdot p^{0}} (1+x)a=(1+x)ak⋅pk+ak−1⋅pk−1+...+a1⋅p1+a0⋅p0
= ( 1 + x ) a k ⋅ p k ⋅ ( 1 + x ) a k − 1 ⋅ p k − 1 ⋅ . . . ⋅ ( 1 + x ) a 1 ⋅ p 1 ⋅ ( 1 + x ) a 0 ⋅ p 0 = (1 + x)^{a_{k}\cdot p^{k}} \cdot (1 + x)^{a_{k - 1}\cdot p^{k - 1}} \cdot \;...\; \cdot (1 + x)^{a_{1}\cdot p^{1}} \cdot (1 + x)^{a_{0}\cdot p^{0}} =(1+x)ak⋅pk⋅(1+x)ak−1⋅pk−1⋅...⋅(1+x)a1⋅p1⋅(1+x)a0⋅p0
≡ ( 1 + x p k ) a k ⋅ ( 1 + x p k − 1 ) a k − 1 ⋅ . . . ⋅ ( 1 + x p 1 ) a 1 ⋅ ( 1 + x p 0 ) a 0 ( m o d p ) \equiv (1 + x^{p^k})^{a_k}\cdot (1 + x^{p^{k-1}})^{a_{k - 1}}\cdot \;...\;\cdot (1 + x^{p^{1}})^{a_{1}}\cdot (1 + x^{p^{0}})^{a_{0}} \quad(mod \;\;p) ≡(1+xpk)ak⋅(1+xpk−1)ak−1⋅...⋅(1+xp1)a1⋅(1+xp0)a0(modp)
我们想求得 C a b C^{b}_{a} Cab,其在等式左边即为 x b x^b xb 的系数。
在等式右边也为 x b x^{b} xb 的系数,由 b b b 的 p p p 进制可得: x b = x b k ⋅ p k + b k − 1 ⋅ p k − 1 + . . . + b 1 ⋅ p 1 + b 0 ⋅ p 0 x^b = x^{b_{k}\cdot p^{k} + b_{k - 1}\cdot p^{k - 1} +\;...\;+ b_{1}\cdot p^{1} + b_{0}\cdot p^{0}} xb=xbk⋅pk+bk−1⋅pk−1+...+b1⋅p1+b0⋅p0
显然, x b k ⋅ p k x^{b_k\cdot p^k} xbk⋅pk 项在 ( 1 + x p k ) a k (1 + x^{p^k})^{a_k} (1+xpk)ak 中是 C a k b k ⋅ x b k ⋅ p k C_{a_k}^{b_k}\cdot x^{b_k\cdot p^k} Cakbk⋅xbk⋅pk ,同理 x b k − 1 ⋅ p k − 1 x^{b_{k-1}\cdot p^{k-1}} xbk−1⋅pk−1 项在 ( 1 + x p k − 1 ) a k − 1 (1 + x^{p^{k-1}})^{a_{k-1}} (1+xpk−1)ak−1 中是 C a k − 1 b k − 1 ⋅ x b k − 1 ⋅ p k − 1 C_{a_{k-1}}^{b_{k-1}}\cdot x^{b_{k-1}\cdot p^{k-1}} Cak−1bk−1⋅xbk−1⋅pk−1,同理可求得 x b x^b xb 的系数为: C a k b k ⋅ C a k − 1 b k − 1 ⋅ . . . ⋅ C a 1 b 1 ⋅ C a 0 b 0 C_{a_k}^{b_k}\cdot C_{a_{k-1}}^{b_{k-1}}\cdot \;...\;\cdot C_{a_{1}}^{b_{1}}\cdot C_{a_{0}}^{b_{0}} Cakbk⋅Cak−1bk−1⋅...⋅Ca1b1⋅Ca0b0
因此可得: C a b ≡ C a k b k ⋅ C a k − 1 b k − 1 ⋅ . . . ⋅ C a 1 b 1 ⋅ C a 0 b 0 ( m o d p ) C^{b}_{a} \equiv C_{a_k}^{b_k}\cdot C_{a_{k-1}}^{b_{k-1}}\cdot \;...\;\cdot C_{a_{1}}^{b_{1}}\cdot C_{a_{0}}^{b_{0}} \quad(mod \;\;p) Cab≡Cakbk⋅Cak−1bk−1⋅...⋅Ca1b1⋅Ca0b0(modp)
因为 a 、 b a、b a、b 是 p p p 进制数,所以有 a 0 = a m o d p a_0 = a \;\;mod\;\; p a0=amodp、 b 0 = b m o d p b_0 = b \;\;mod\;\; p b0=bmodp
我们让 a 、 b a、b a、b 在 p p p 进制中右移一位,即 ⌊ a p ⌋ 、 ⌊ b p ⌋ \lfloor \frac{a}{p} \rfloor、\lfloor \frac{b}{p} \rfloor ⌊pa⌋、⌊pb⌋
对于 ⌊ a p ⌋ 、 ⌊ b p ⌋ \lfloor \frac{a}{p} \rfloor、\lfloor \frac{b}{p} \rfloor ⌊pa⌋、⌊pb⌋ 重复上面最开始的步骤,同样可以得到: C ⌊ a p ⌋ ⌊ b p ⌋ ≡ C a k b k ⋅ C a k − 1 b k − 1 ⋅ . . . ⋅ C a 1 b 1 ( m o d p ) C^{\lfloor \frac{b}{p} \rfloor}_{\lfloor \frac{a}{p} \rfloor} \equiv C_{a_k}^{b_k}\cdot C_{a_{k-1}}^{b_{k-1}}\cdot \;...\;\cdot C_{a_{1}}^{b_{1}} \quad(mod \;\;p) C⌊pa⌋⌊pb⌋≡Cakbk⋅Cak−1bk−1⋅...⋅Ca1b1(modp)
因此: C a b ≡ C ⌊ a p ⌋ ⌊ b p ⌋ ⋅ C a m o d p b m o d p ( m o d p ) C^{b}_{a} \equiv C^{\lfloor \frac{b}{p} \rfloor}_{\lfloor \frac{a}{p} \rfloor}\cdot C_{ a \;\;mod\;\; p}^{ b \;\;mod\;\; p} \quad(mod \;\;p) Cab≡C⌊pa⌋⌊pb⌋⋅Camodpbmodp(modp)
综上得证。
假设:
{ ⌊ a p ⌋ = q a ⌊ b p ⌋ = q b , { a m o d p = r a b m o d p = r b \begin{cases} \lfloor \frac{a}{p} \rfloor = q_a \\ \lfloor \frac{b}{p} \rfloor = q_b \end{cases} , \;\; \begin{cases} a \;\;mod \;\; p = r_a \\ b \;\;mod \;\; p = r_b \end{cases} {⌊pa⌋=qa⌊pb⌋=qb,{amodp=rabmodp=rb
所以有:
{ a = q a × p + r a b = q b × p + r b 因 为 ( 二 项 式 定 理 ) : ( 1 + x ) a = ∑ k = 0 a C a k ⋅ x k 且 : ( 1 + x ) a = ( 1 + x ) q a ⋅ p + r a = ( 1 + x ) q a ⋅ p ⋅ ( 1 + x ) r a ≡ ( 1 + x p ) q a ⋅ ( 1 + x ) r a ( m o d p ) ( 因 为 ( 1 + x ) p ≡ ( 1 + x p ) ( m o d p ) ) = ∑ i = 0 q a C q a i ⋅ x i ⋅ p ⋅ ∑ j = 0 r A C r a j ⋅ x j = ∑ i = 0 q a ∑ j = 0 r A C q a i ⋅ C r a j ⋅ x i ⋅ p + j \begin{cases} a = q_a \times p + r_a \\ b = q_b \times p + r_b \\ \end{cases} \\ 因为(二项式定理): (1 + x) ^ a = \sum^a_{k = 0} C_a^{k} \cdot x^k \\ 且: (1 + x) ^ a = (1 + x)^{q_a \cdot p + r_a} \\ = (1 + x)^{q_a \cdot p} \cdot (1 + x) ^ {r_a} \\ \equiv (1 + x^p)^{q_a} \cdot (1 + x) ^{r_a} \quad(mod \;\;p) \\ (因为\;(1 + x)^ p \equiv (1 + x^p) \quad(mod \;\;p)) \\ = \sum^{q_a}_{i = 0} C^{\ i}_{q_a} \cdot x^{i \cdot p} \cdot \sum^{r_A}_{j = 0} C^{\ j}_{r_a} \cdot x^{j} \\ = \sum^{q_a}_{i = 0} \sum^{r_A}_{j = 0}C^{\ i}_{q_a} \cdot C^{\ j}_{r_a} \cdot x^{i \cdot p + j} {a=qa×p+rab=qb×p+rb因为(二项式定理):(1+x)a=k=0∑aCak⋅xk且:(1+x)a=(1+x)qa⋅p+ra=(1+x)qa⋅p⋅(1+x)ra≡(1+xp)qa⋅(1+x)ra(modp)(因为(1+x)p≡(1+xp)(modp))=i=0∑qaCqa i⋅xi⋅p⋅j=0∑rACra j⋅xj=i=0∑qaj=0∑rACqa i⋅Cra j⋅xi⋅p+j
令 k = i ⋅ p + j k = i \cdot p + j k=i⋅p+j,则 i = ⌊ k p ⌋ , j = k m o d p i = \lfloor \frac{k}{p} \rfloor,\quad j = k \;\;mod \;\; p i=⌊pk⌋,j=kmodp,所以上式等于:
= ∑ k = 0 a C q a ⌊ k p ⌋ ⋅ C r a k m o d p ⋅ x k = \sum^a_{k = 0} C^{\lfloor \frac{k}{p} \rfloor}_{q_a} \cdot C^{\ k \;\;mod \;\; p}_{r_a} \cdot x^{k} =k=0∑aCqa⌊pk⌋⋅Cra kmodp⋅xk
综上有:
( 1 + x ) a ≡ ∑ k = 0 a C q a ⌊ k p ⌋ ⋅ C r a k m o d p ⋅ x k ( m o d p ) 且 : ( 1 + x ) a = ∑ k = 0 a C a k ⋅ x k 故 : ∑ k = 0 a C a k ⋅ x k ≡ ∑ k = 0 a C q a ⌊ k p ⌋ ⋅ C r a k m o d p ⋅ x k ( m o d p ) (1 + x) ^ a \equiv \sum^a_{k = 0} C^{\lfloor \frac{k}{p} \rfloor}_{q_a} \cdot C^{\ k \;\;mod \;\; p}_{r_a} \cdot x^{k} \quad(mod \;\;p) \\ 且: (1 + x) ^ a = \sum^a_{k = 0} C_a^{k} \cdot x^k \\ 故:\sum^a_{k = 0} C_a^{k} \cdot x^k \equiv \sum^a_{k = 0} C^{\lfloor \frac{k}{p} \rfloor}_{q_a} \cdot C^{\ k \;\;mod \;\; p}_{r_a} \cdot x^{k} \quad(mod \;\;p) \\ (1+x)a≡k=0∑aCqa⌊pk⌋⋅Cra kmodp⋅xk(modp)且:(1+x)a=k=0∑aCak⋅xk故:k=0∑aCak⋅xk≡k=0∑aCqa⌊pk⌋⋅Cra kmodp⋅xk(modp)
对于其中某一项 k = b k = b k=b 有:
C a b ≡ C q a ⌊ b p ⌋ ⋅ C r a b m o d p ( m o d p ) 即 : C a b ≡ C ⌊ a p ⌋ ⌊ b p ⌋ ⋅ C a m o d p b m o d p ( m o d p ) 得 证 C_a^{b} \equiv C^{\lfloor \frac{b}{p} \rfloor}_{q_a} \cdot C^{\ b \;\;mod \;\; p}_{r_a} \quad (mod \;\;p) \\ 即:\\ C_a^{b} \equiv C^{\lfloor \frac{b}{p} \rfloor}_{\lfloor \frac{a}{p} \rfloor} \cdot C^{\ b \;\;mod \;\; p}_{\ a \;\;mod \;\; p} \quad (mod \;\;p) \\ 得证 Cab≡Cqa⌊pb⌋⋅Cra bmodp(modp)即:Cab≡C⌊pa⌋⌊pb⌋⋅C amodp bmodp(modp)得证
#include
using namespace std;
typedef long long LL;
int readInt() {
int res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res =res * 10 + ch - '0';
ch = getchar();
}
return res;
}
LL readLong() {
LL res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res = res * 10 + ch - '0';
ch = getchar();
}
return res;
}
int quick_power(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = 1ll * res * a % p;
k >>= 1;
a = 1ll * a * a % p;
}
return res;
}
int C(int a, int b, int p) {
int res = 1;
for (int i = 1, j = a; i <= b; i ++, j --) {
res = 1ll * res * j % p;
res = 1ll * res * quick_power(i, p - 2, p) % p;
}
return res;
}
int lucas(LL a, LL b, int p) {
if (a < p && b < p) return C(a, b, p);
return 1ll * C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
}
int main() {
int n;
n = readInt();
while (n -- ) {
LL a, b;
int p;
a = readLong(), b = readLong(), p = readInt();
// cout << a << " " << b << " " << p << endl;
cout << lucas(a, b, p) << endl;
}
return 0;
}
为什么可以这样做呢?
:每个数都可以写成这样的形式, N = p 1 a 1 × p 2 a 2 × p 3 a 3 × . . . × p k a k ( p i 是 质 数 ) N = p_1^{a_1} \times p_2^{a_2} \times p_3^{a_3} \times ... \times p_k^{a_k} (p_i 是质数) N=p1a1×p2a2×p3a3×...×pkak(pi是质数) 。所以 n ! n! n! 只需要用 ⌊ n p ⌋ ⌊\frac{n}{p}⌋ ⌊pn⌋ 即可得到阶乘中某个质数的次数 p k p^k pk。
#include
#include
using namespace std;
const int N = 5010;
int primes[N], cnt;
bool st[N];
int sum[N];
int read() {
int res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res = res * 10 + ch - '0';
ch = getchar();
}
return res;
}
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 n, int p) {
int res = 0;
while (n) {
res += n / p;
n /= p;
}
return res;
}
void mul(vector<int>& a, int b) {
int t = 0;
for (int i = 0; i < a.size(); ++ i ) {
t += a[i] * b;
a[i] = t % 10;
t /= 10;
}
while (t) {
a.push_back(t % 10);
t /= 10;
}
return;
}
int main() {
int a, b;
a = read(), b = read();
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) {
mul(res, primes[i]);
}
}
for (int i = res.size() - 1; i >= 0; i -- ) printf("%d", res[i]);
puts("");
return 0;
}
每一个越过中线走到距离中线最近的不合法路径上 l 2 l_2 l2 的不合法路径 l 1 l_1 l1,都可以把 l 1 l_1 l1 和 l 2 l_2 l2 的交叉点处到终点位置的路径 l 3 l_3 l3 沿着中线 y = x y = x y=x 轴对称,终点始终为 ( n − 1 , n + 1 ) (n - 1, n + 1) (n−1,n+1)。即每一条不合法路径都可以转化成到 ( n − 1 , n + 1 ) (n - 1, n + 1) (n−1,n+1) 的一条路径(同理,因为 原点到 ( n − 1 , n + 1 ) (n - 1, n + 1) (n−1,n+1) 必须经过中线,一定和中线有交点,也一定可以转化成一条到 ( n , n ) (n, n) (n,n) 的非法路径)。
因此答案为: C 2 ⋅ n n − C 2 ⋅ n n − 1 C^{n}_{2\cdot n} - C^{n - 1}_{2\cdot n} C2⋅nn−C2⋅nn−1
可化简为 C 2 ∗ n n n + 1 \frac{C_{2*n}^{n}}{n + 1} n+1C2∗nn
#include
using namespace std;
typedef long long LL;
const int mod = 1e9 + 7;
int read() {
int res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res = res * 10 + ch -'0';
ch = getchar();
}
return res;
}
int quick_power(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = 1ll * res * a % p;
k >>= 1;
a = 1ll * a * a % p;
}
return res;
}
int main() {
int n;
n = read();
int res = 1;
for (int i = 2*n, j = 1; j <= n; i --, j ++) {
res = 1ll * res * i % mod;
res = 1ll * res * quick_power(j, mod - 2, mod) % mod;
}
res = 1ll * res * quick_power(n + 1, mod - 2, mod) % mod;
cout << res << endl;
return 0;
}
所有方案数为:所有单个集合的总方案数 减 所有两个集合的组合中重复加的数 加 所有三个集合的组合中重复减去的数 ·····
本题中所有单个集合的总方案项数为 C m 1 C_{m}^{1} Cm1,所有两个集合的组合中重复加的项数为 C m 2 C_{m}^{2} Cm2,······
总共要加减多少项呢? 因为除了所有的集合都不选之外,其他所有任意种组合我们都选了,所以总共组合的项数为 2 n − 1 2^{n} - 1 2n−1 (所以有, ∑ i = 1 m C m i = 2 n − 1 \sum_{i = 1}^{m}C_{m}^{i} = 2^n - 1 ∑i=1mCmi=2n−1)。对于每一种组合如果他包含奇数个单个集合,我们就应该加上这个这种组合的方案数,否则减去。
每一项如何计算呢? 我们可以用位运算枚举每一种组合,记录一下每一种组合的值 v a l u e value value(当且仅当他小于 n n n 才算有效组合),那么这种组合的方案数为 ⌊ n v a l u e ⌋ \lfloor \frac{n}{value} \rfloor ⌊valuen⌋ 。
#include
using namespace std;
const int N = 20;
int p[N];
int read() {
int res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res = res * 10 + ch - '0';
ch = getchar();
}
return res;
}
int main() {
int n, m;
n = read();
m = read();
for (int i = 0; i < m; ++ i ) p[i] = read();
int res = 0;
for (int i = 1; i < 1 << m; ++ i ) {
int t = 1, cnt = 0;
for (int j = 0; j < m; ++ j ) {
if (i & 1 << j) {
if (1ll * t * p[j] > n) {
t = -1;
break;
}
cnt ++;
t = t * p[j];
}
}
if (t != -1) {
if (cnt % 2) res += n / t;
else res -= n / t;
}
}
cout << res << endl;
return 0;
}
由题可知,必败状态是所有堆为 0,其异或和为 0。当异或和为 0 无论如何操作都是异或和不为 0 的状态(因为 不可以不拿);当异或和不为 0 的时候,设异或和值为 x x x, x x x 的二进制最高位为第 k k k 位,则一定有一堆的第 k k k 位为 1,设其数量为 a i a_i ai ,在 a i a_i ai 中拿去 a i − ( a i ⨁ x ) a_i - (a_i \bigoplus x) ai−(ai⨁x) 个,则该堆变为 a i ⨁ x a_i \bigoplus x ai⨁x 个,所有堆的异或和就会变为 0。所以,当异或和不为 0 的时候,总可以让对手面临异或和为 0 的状态,且数量一直在减少,对手一定会遇到全为 0 的状态。因此只要先手遇到异或和不为 0 ,必胜,否则必败。
#include
using namespace std;
int read() {
int res = 0;
char ch = getchar();
while (ch <= '9' && ch >= '0') {
res = res * 10 + ch - '0';
ch = getchar();
}
return res;
}
int main() {
int n;
n = read();
int res = 0;
for (int i = 0; i < n; ++ i ) res = res ^ read();
if (res) puts("Yes");
else puts("No");
return 0;
}
可以只看奇数台阶,那么就是经典的 NIM游戏:
#include
using namespace std;
int main() {
int n;
cin >> n;
int res = 0;
for (int i = 0; i < n; ++ i ) {
int x;
cin >> x;
if (i % 2 == 0) res ^= x;
}
if (res) puts("Yes");
else puts("No");
return 0;
}
SG 函数:
**SG函数的意义:**当有多个图的时候,我们可以把每个图的起点值异或起来,然后用 NIM游戏 的方法得到必胜和必败态。
为了防止运算 sg 达到指数级别,这里使用了**记忆化搜索**, 即把中间的结果保存下来。
// 对于数据不规范的少用自己写的 read
#include
#include
#include
using namespace std;
const int N = 110, M = 10010;
int s[N], f[M];
int n, k;
int sg(int x) {
if (f[x] != -1) return f[x];
unordered_set<int> state;
for (int i = 0; i < k; ++ i ) {
if (x >= s[i]) state.insert(sg(x - s[i]));
// 如果可以采用这个拿取得方案,就求一下拿取之后得剩余得数量得 sg 函数。
// 当前 sg 函数就是看看当前不能走到得最小的自然数。
}
for (int i = 0; ; ++ i ) {
if (!state.count(i))
return f[x] = i;
}
}
int main() {
cin >> k;
for (int i = 0; i < k; ++ i ) cin >> s[i];
cin >> n;
int res = 0;
memset(f, -1, sizeof f);
for (int i = 0; i < n; ++ i ) {
int x;
cin >> x;
res = res ^ sg(x);
}
if (res) puts("Yes");
else puts("No");
return 0;
}
有一个很强的性质: S G ( b 1 , b 2 ) = S G ( b 1 ) ⨁ S G ( b 2 ) SG(b_1, b_2) = SG(b_1) \;\bigoplus\; SG(b_2) SG(b1,b2)=SG(b1)⨁SG(b2)
把每一种可以拆分的局面的 S G SG SG 值都统计一下即可(且, i i i 和 j j j 的值交换没有意义)。
#include
#include
#include
using namespace std;
const int N = 110;
int n;
int f[N];
int sg(int x) {
if (f[x] != -1) return f[x];
unordered_set<int> state;
for (int i = 0; i < x; ++ i ) {
for (int j = 0; j <= i; ++ j ) {
state.insert(sg(i) ^ sg(j));
}
}
for (int i = 0; ; ++ i ) {
if (!state.count(i)) {
return f[x] = i;
}
}
}
int main() {
cin >> n;
int res = 0;
memset(f, -1, sizeof f);
for (int i = 0; i < n; ++ i ) {
int x;
cin >> x;
res ^= sg(x);
}
if (res) puts("Yes");
else puts("No");
return 0;
}