• 数学知识总结


    素数

    质数/素数定义

            在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数,或者叫素数


    判断素数(试除法)

    约数有一个重要的性质:

    假设n代表数字,d代表n的一个约数

    即d能整除n(d|n)

    那么n/d为n的另外一个约数

    即n/d能整除d(n/d | n)

    简单思路:

    模板:

    1. bool is_prime(int n)
    2. {
    3. if (n <= 1) return false;
    4. for (int i = 2; i <= n / i; i ++ )
    5. if (n % i == 0) return false;
    6. return true;
    7. }

     866. 试除法判定质数

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. bool is_prime(int n)
    6. {
    7. if (n <= 1) return false;
    8. for (int i = 2; i <= n / i; i ++ )
    9. if (n % i == 0) return false;
    10. return true;
    11. }
    12. int main()
    13. {
    14. int n;
    15. cin >> n;
    16. while (n --)
    17. {
    18. int a;
    19. cin >> a;
    20. if (is_prime(a)) puts("Yes");
    21. else puts("No");
    22. }
    23. return 0;
    24. }


    分解质因数(试除法)

    从小到大枚举所有的数

    为什么不需要判断是否为质因子

    因为i是n的因子,即n是i的倍数,当枚举的到i时,n中已经不包含有2~i-1的因子,即i中也不包含,2~i-1的因子,所以i是质数

    n中最多只包括一个大于sqrt(n)的质因子

    如果最后剩下一个大于1的数,那就是大于sqrt(n)的质因子

    模板:

    1. void divide(int n)
    2. {
    3. for (int i = 2; i <= n / i; i ++ )
    4. {
    5. if (n % i == 0)
    6. {
    7. int cnt = 0;
    8. while (n % i == 0)
    9. {
    10. cnt ++;
    11. n /= i;
    12. }
    13. printf("%d %d\n", i, cnt);
    14. }
    15. }
    16. if (n > 1) printf("%d %d\n", n, 1);
    17. printf("\n");
    18. }

    acwing 867. 分解质因数

    1. ​​​​​​#include
    2. #include
    3. using namespace std;
    4. void divide(int n)
    5. {
    6. for (int i = 2; i <= n / i; i ++ )
    7. {
    8. if (n % i == 0)
    9. {
    10. int cnt = 0;
    11. while (n % i == 0)
    12. {
    13. cnt ++;
    14. n /= i;
    15. }
    16. printf("%d %d\n", i, cnt);
    17. }
    18. }
    19. if (n > 1) printf("%d %d\n", n, 1);
    20. printf("\n");
    21. }
    22. int main()
    23. {
    24. int n;
    25. scanf("%d", &n);
    26. while (n --)
    27. {
    28. int a;
    29. scanf("%d", &a);
    30. divide(a);
    31. }
    32. return 0;
    33. }


      埃氏筛法

    算法思路:

     模板:

    1. void gets_prime(int n)
    2. {
    3. for (int i = 2; i <= n; i ++ )
    4. {
    5. if (!st[i])
    6. {
    7. cnt ++;
    8. for (int j = i * 2; j <= n; j += i) st[j] = true;
    9. }
    10. }
    11. }

    868. 筛质数

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N = 1000010;
    6. bool st[N];
    7. int cnt;
    8. void gets_prime(int n)
    9. {
    10. for (int i = 2; i <= n; i ++ )
    11. {
    12. if (!st[i])
    13. {
    14. cnt ++;
    15. for (int j = i * 2; j <= n; j += i) st[j] = true;
    16. }
    17. }
    18. }
    19. int main()
    20. {
    21. int n;
    22. cin >> n;
    23. gets_prime(n);
    24. cout << cnt << endl;
    25. }


     线性筛(欧拉筛法)

    算法思路:

    模板:

    1. void oula(int n)
    2. {
    3. for (int i = 2; i <= n; i ++ )
    4. {
    5. if (!st[i]) primes[cnt ++] = i;
    6. for (int j = 0; primes[j] <= n / i; j ++ )
    7. {
    8. st[i * primes[j]] = true;
    9. if (i % primes[j] == 0) break;
    10. }
    11. }
    12. }

     868. 筛质数

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N = 1000010;
    6. int primes[N];
    7. bool st[N];
    8. int cnt;
    9. void oula(int n)
    10. {
    11. for (int i = 2; i <= n; i ++ )
    12. {
    13. if (!st[i]) primes[cnt ++] = i;
    14. for (int j = 0; primes[j] <= n / i; j ++ )
    15. {
    16. st[i * primes[j]] = true;
    17. if (i % primes[j] == 0) break;
    18. }
    19. }
    20. }
    21. int main()
    22. {
    23. int n;
    24. cin >> n;
    25. oula(n);
    26. cout << cnt << endl;
    27. }




    约数

    试除法

    思路:

    模板:

    1. int res = 0;
    2. for (int i = 1; i <= n / i; i ++ )
    3. {
    4. if (n % i == 0)
    5. {
    6. if (i == n / i) res += 2;
    7. else res += 1;
    8. }
    9. }

     —————————————这就是纯暴力解法,时间复杂度高——————————————


    通过算数基本定理来求约数个数

    思路:

     模板:

    1. unordered_map<int, int> prime;//prime[质因子] = 次方
    2. for (int i = 2; i <= x / i; i ++ )//分解质因数
    3. {
    4. if (x % i == 0)
    5. {
    6. while (x % i == 0)
    7. {
    8. x /= i;
    9. prime[i] ++;
    10. }
    11. }
    12. }
    13. if (x > 1) prime[x] ++;
    14. long long res = 1;
    15. for (auto it = prime.begin(); it != prime.end(); it ++)
    16. {
    17. res = res * (prime->second + 1);
    18. }

    870. 约数个数

    输入样例: 

    1. 3
    2. 2
    3. 6
    4. 8

    代码展示:

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. typedef long long LL;
    7. const int mod = 1e9 + 7;
    8. int main()
    9. {
    10. int n;
    11. scanf("%d",&n);
    12. unordered_map<int, int> prime;
    13. while (n --)
    14. {
    15. int x;
    16. cin >> x;
    17. for (int i = 2; i <= x / i; i ++ )
    18. {
    19. while (x % i == 0)
    20. {
    21. x /= i;
    22. prime[i] ++;
    23. }
    24. }
    25. if (x > 1) prime[x] ++;
    26. }
    27. LL res = 1;
    28. for (auto p : prime) res = res * (p.second + 1) % mod;
    29. printf("%lld\n",res);
    30. }

    ————————————————————next—————————————————————


    通过算术基本定理求约数之和 

    思路:

    模板: 

    1. unordered_map<int, int> prime;//prime[质因子] = 次方
    2. for (int i = 2; i <= x / i; i ++ )//分解质因数
    3. {
    4. if (x % i == 0)
    5. {
    6. while (x % i == 0)
    7. {
    8. x /= i;
    9. prime[i] ++;
    10. }
    11. }
    12. }
    13. if (x > 1) prime[x] ++;
    14. long long res = 1;
    15. for (auto it = prime.begin(); it != prime.end(); it ++)
    16. {
    17. int a = it->first, b = it->second;
    18. long long t = 1;
    19. for (int i = 0; i < b; i ++) t = t * a + 1;
    20. res = res * t;
    21. }

    871. 约数之和

    输入样例:

    1. 3
    2. 2
    3. 6
    4. 8

     代码展示:

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. typedef long long LL;
    7. const int mod = 1e9 + 7;
    8. int main()
    9. {
    10. int n;
    11. scanf("%d",&n);
    12. unordered_map<int, int> prime;
    13. while (n --)
    14. {
    15. int x;
    16. cin >> x;
    17. for (int i = 2; i <= x / i; i ++ )
    18. {
    19. while (x % i == 0)
    20. {
    21. prime[i] ++;
    22. x /= i;
    23. }
    24. }
    25. if (x > 1) prime[x] ++;
    26. }
    27. LL res = 1;
    28. for (auto p : prime)
    29. {
    30. int a = p.first, b = p.second;
    31. LL t = 1;
    32. for (int i = 0; i < b; i ++ ) t = (t * a + 1) % mod;
    33. res = res * t % mod;
    34. }
    35. cout << res << endl;
    36. }

    ———————————————————next—————————————————————


    最大公约数 

    思路:

    模板: 

    1. int gcd(int a, int b)
    2. {
    3. return b ? gcd(b, a % b) : a;
    4. }

    输入样例:

    1. 2
    2. 3 6
    3. 4 6

     AcWing 872. 最大公约数

    代码展示:

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int gcd(int a, int b)
    6. {
    7. return b ? gcd(b, a % b) : a;
    8. }
    9. int main()
    10. {
    11. int n;
    12. cin >> n;
    13. while (n --)
    14. {
    15. int a, b;
    16. cin >> a >> b;
    17. int ans = gcd(a, b);
    18. cout << ans << endl;
    19. }
    20. return 0;
    21. }

    欧拉函数

    基本思路:

     

     模板(暴力):

    1. long long res = x;
    2. for (int i = 2; i <= x / i; i ++ )
    3. {
    4. if (x % i == 0)
    5. {
    6. while (x % i == 0) x /= i;
    7. res = res * (i - 1) / i;
    8. }
    9. }
    10. if (x > 1) res = res * (x - 1) / x;

    Acwing873. 欧拉函数

    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. int n;
    7. cin >> n;
    8. while (n --)
    9. {
    10. int x;
    11. cin >> x;
    12. long long res = x;
    13. for (int i = 2; i <= x / i; i ++ )
    14. {
    15. if (x % i == 0)
    16. {
    17. while (x % i == 0) x /= i;
    18. res = res * (i - 1) / i;
    19. }
    20. }
    21. if (x > 1) res = res * (x - 1) / x;
    22. cout << res << endl;
    23. }
    24. }

     模板(线性筛法):

    1. const int N = 1e6 + 10;
    2. int primes[N], cnt;
    3. bool st[N];
    4. int phi[N];
    5. void oula(int x)
    6. {
    7. phi[1] = 1;
    8. for (int i = 2; i <= x; i ++)
    9. {
    10. if (!st[i])
    11. {
    12. primes[cnt ++] = i;
    13. phi[i] = i - 1;
    14. }
    15. for (int j = 0; primes[j] <= x / i; j ++)
    16. {
    17. st[i * primes[j]] = true;
    18. phi[i * primes[j]] = primes[j] * phi[i] * (primes[j] - 1) / primes[j];
    19. if (i % primes[j] == 0)
    20. {
    21. phi[i * primes[j]] = primes[j] * phi[i];
    22. break;
    23. }
    24. }
    25. }
    26. }

    运用线性筛算欧拉函数思路:

    Acwing874. 筛法求欧拉函数

    1. #include
    2. #include
    3. using namespace std;
    4. const int N = 1e6 + 10;
    5. int primes[N], cnt;
    6. bool st[N];
    7. int phi[N];
    8. void oula(int x)
    9. {
    10. phi[1] = 1;
    11. for (int i = 2; i <= x; i ++)
    12. {
    13. if (!st[i])
    14. {
    15. primes[cnt ++] = i;
    16. phi[i] = i - 1;
    17. }
    18. for (int j = 0; primes[j] <= x / i; j ++)
    19. {
    20. st[i * primes[j]] = true;
    21. phi[i * primes[j]] = primes[j] * phi[i] * (primes[j] - 1) / primes[j];
    22. if (i % primes[j] == 0)
    23. {
    24. phi[i * primes[j]] = primes[j] * phi[i];
    25. break;
    26. }
    27. }
    28. }
    29. }
    30. int main()
    31. {
    32. int n;
    33. cin >> n;
    34. oula(n);
    35. long long res = 0;
    36. for (int i = 1; i <= n; i ++) res += phi[i];
    37. cout << res << endl;
    38. }




    快速幂

    基本思路:

    模板:

    1. typedef long long LL;
    2. LL qmi(int a, int k, int p)
    3. {
    4. LL res = 1;
    5. while (k)
    6. {
    7. if (k & 1) res = res * a % p;
    8. k >>= 1;
    9. a = (LL)a * a % p;
    10. }
    11. return res;
    12. }

     AcWing 875. 快速幂

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. typedef long long LL;
    6. LL qmi(int a, int k, int p)
    7. {
    8. LL res = 1;
    9. while (k)
    10. {
    11. if (k & 1) res = res * a % p;
    12. k >>= 1;
    13. a = (LL)a * a % p;
    14. }
    15. return res;
    16. }
    17. int main()
    18. {
    19. int n;
    20. cin >> n;
    21. while (n --)
    22. {
    23. int a, k, p;
    24. cin >> a >> k >> p;
    25. cout << qmi(a, k, p) << endl;
    26. }
    27. }

     快速幂求逆元思路:

     AcWing 876. 快速幂求逆元  

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long LL;
    5. LL qmi(int a, int k, int p)
    6. {
    7. LL res = 1;
    8. while (k)
    9. {
    10. if (k & 1) res = res * a % p;
    11. k >>= 1;
    12. a = (LL) a * a % p;
    13. }
    14. return res;
    15. }
    16. int main()
    17. {
    18. int n;
    19. cin >> n;
    20. while (n --)
    21. {
    22. int a, p;
    23. cin >> a >> p;
    24. if (a % p) cout << qmi(a, p - 2, p) << endl;
    25. else puts("impossible");
    26. }
    27. }




    扩展欧几里得

    基本思路:

    模板:

    1. int exgcd(int a, int b, int &x, int &y)
    2. {
    3. if (b == 0)
    4. {
    5. x = 1, y = 0;
    6. return a;
    7. }
    8. int d = exgcd(b, a % b, y, x);
    9. y = y - a / b * x;
    10. return d;
    11. }

     Acwing877. 扩展欧几里得算法

    1. #include
    2. #include
    3. using namespace std;
    4. int exgcd(int a, int b, int &x, int &y)
    5. {
    6. if (b == 0)
    7. {
    8. x = 1, y = 0;
    9. return a;
    10. }
    11. int d = exgcd(b, a % b, y, x);
    12. y = y - a / b * x;
    13. return d;
    14. }
    15. int main()
    16. {
    17. int n;
    18. cin >> n;
    19. while (n --)
    20. {
    21. int a, b, x, y;
    22. cin >> a >> b;
    23. exgcd(a, b, x, y);
    24. cout << x << ' ' << y << endl;
    25. }
    26. }

     裴蜀定理:

    对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性不定方程(称为裴蜀等式):若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。

     Acwing878. 线性同余方程

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long LL;
    5. int exgcd(int a, int b, int &x, int &y)
    6. {
    7. if (b == 0)
    8. {
    9. x = 1, y = 0;
    10. return a;
    11. }
    12. int d = exgcd(b, a % b, y, x);
    13. y = y - a / b * x;
    14. return d;
    15. }
    16. int main()
    17. {
    18. int n;
    19. cin >> n;
    20. while (n --)
    21. {
    22. int a, b, m, x, y;
    23. cin >> a >> b >> m;
    24. int d = exgcd(a, m, x, y);
    25. if (b % d) puts("impossible");
    26. else cout << (LL)x * (b / d) % m << endl;
    27. }
    28. }



    中国剩余定理

    定义(来自百度百科):

    AcWing 204. 表达整数的奇怪方式

    基本思路:

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long LL;
    5. LL exgcd(LL a, LL b, LL &x, LL &y)
    6. {
    7. if (b == 0)
    8. {
    9. x = 1, y = 0;
    10. return a;
    11. }
    12. LL d = exgcd(b, a % b, y, x);
    13. y -= a / b * x;
    14. return d;
    15. }
    16. int main()
    17. {
    18. int n;
    19. cin >> n;
    20. LL a1, m1, x;
    21. cin >> a1 >> m1;
    22. for (int i = 0; i < n - 1; i ++ )
    23. {
    24. LL a2, m2;
    25. cin >> a2 >> m2;
    26. LL k1, k2;
    27. LL d = exgcd(a1, a2, k1, k2);
    28. if ((m2 - m1) % d)
    29. {
    30. x = -1;
    31. break;
    32. }
    33. k1 = k1 * ((m2 - m1) / d);
    34. LL t = a2 / d;
    35. k1 = (k1 % t + t) % t;
    36. x = k1 * a1 + m1;
    37. m1 = k1 * a1 + m1;
    38. a1 = abs(a1 / d * a2);
    39. }
    40. if (x != -1) x = (m1 % a1 + a1) % a1;
    41. cout << x << endl;
    42. }




    求组合数

    方法:

    1. 递推

    动态规划思想:

    AcWing 885. 求组合数 I 

    1. #include
    2. #include
    3. using namespace std;
    4. const int N = 2010, mod = 1e9 + 7;
    5. int C[N][N];
    6. void dp()
    7. {
    8. for (int i = 0; i < N; i ++ )
    9. {
    10. for (int j = 0; j <= i; j ++ )
    11. {
    12. if (!j) C[i][j] = 1; //从i件物品中选取0件的方案数为1
    13. else C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod; //状态转移方程
    14. }
    15. }
    16. }
    17. int main()
    18. {
    19. int n;
    20. cin >> n;
    21. dp();
    22. while (n --)
    23. {
    24. int a, b;
    25. cin >> a >> b;
    26. cout << C[a][b] << endl;
    27. }
    28. return 0;
    29. }

    2.预处理

    思路:

    模板: 

    1. //快速幂
    2. int qmi(int a, int k, int p)
    3. {
    4. int res = 1;
    5. while (k)
    6. {
    7. if (k & 1) res = (LL)res * a % p;
    8. a = (LL)a * a % p;
    9. k >>= 1;
    10. }
    11. return res;
    12. }
    13. //预处理
    14. void initial()
    15. {
    16. fact[0] = 1;
    17. infact[0] = 1;
    18. for (int i = 1; i < N; i ++)
    19. {
    20. fact[i] = (LL)fact[i - 1] * i % mod;
    21. infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    22. }
    23. }

    AcWing 886. 求组合数 II 

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long LL;
    5. const int N = 1e5 + 10, mod = 1e9 + 7;
    6. int fact[N], infact[N];
    7. //快速幂
    8. int qmi(int a, int k, int p)
    9. {
    10. int res = 1;
    11. while (k)
    12. {
    13. if (k & 1) res = (LL)res * a % p;
    14. a = (LL)a * a % p;
    15. k >>= 1;
    16. }
    17. return res;
    18. }
    19. //预处理
    20. void initial()
    21. {
    22. fact[0] = 1;
    23. infact[0] = 1;
    24. for (int i = 1; i < N; i ++)
    25. {
    26. fact[i] = (LL)fact[i - 1] * i % mod;
    27. infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    28. }
    29. }
    30. int main()
    31. {
    32. int n;
    33. cin >> n;
    34. initial();
    35. while (n --)
    36. {
    37. int a, b;
    38. cin >> a >> b;
    39. int res = (LL)fact[a] * infact[b] % mod * infact[a - b] % mod;
    40. cout << res << endl;
    41. }
    42. }

    3.卢卡斯(lucas)

    定义:

                    Lucas定理是用来求 c(n,m) mod p,p为素数的值。

    公式:

                    C(n,m)%p=C(n/p,m/p)*C(n%p,m%p)%p

    AcWing 887. 求组合数 III

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long LL;
    5. //快速幂
    6. int qmi(int a, int k, int p)
    7. {
    8. int res = 1;
    9. while (k)
    10. {
    11. if (k & 1) res = (LL)res * a % p;
    12. a = (LL)a * a % p;
    13. k >>= 1;
    14. }
    15. return res;
    16. }
    17. //求组合数
    18. int C(int a, int b, int p)
    19. {
    20. if (b > a) return 0;
    21. int res = 1;
    22. for (int i = 1, j = a; i <= b; i ++, j --)
    23. {
    24. res = (LL) res * j % p;
    25. res = (LL) res * qmi(i, p - 2, p) % p;
    26. }
    27. return res;
    28. }
    29. //卢卡斯定理
    30. int lucas(LL a, LL b, int p)
    31. {
    32. if (a < p && b < p) return C(a, b, p);
    33. return (LL)C(a % p, b % p, p) * lucas(a / p, b / p, p) % p;
    34. }
    35. int main()
    36. {
    37. int n;
    38. cin >> n;
    39. while (n --)
    40. {
    41. LL a, b;
    42. int p;
    43. cin >> a >> b >> p;
    44. cout << lucas(a, b, p) << endl;
    45. }
    46. }

    4.高精度

    思路:

    Acwing888. 求组合数 IV

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. const int N = 5010;
    6. int primes[N], cnt;
    7. bool st[N];
    8. int sum[N];
    9. //线性筛(欧拉筛)
    10. void get_primes(int n)
    11. {
    12. for (int i = 2; i <= n; i ++)
    13. {
    14. if (!st[i]) primes[cnt ++] = i;
    15. for (int j = 0; primes[j] <= n / i; j ++)
    16. {
    17. st[i * primes[j]] = true;
    18. if (i % primes[j] == 0) break;
    19. }
    20. }
    21. }
    22. //求n中p的个数
    23. int get(int n, int p)
    24. {
    25. int res = 0;
    26. while (n)
    27. {
    28. res += n / p;
    29. n /= p;
    30. }
    31. return res;
    32. }
    33. //高精度乘法
    34. vector<int> mul(vector<int> a, int b)
    35. {
    36. vector<int> c;
    37. int t = 0;
    38. for (int i = 0; i < a.size(); i ++)
    39. {
    40. t += a[i] * b;
    41. c.push_back(t % 10);
    42. t /= 10;
    43. }
    44. while (t)
    45. {
    46. c.push_back(t % 10);
    47. t /= 10;
    48. }
    49. return c;
    50. }
    51. int main()
    52. {
    53. int a, b;
    54. cin >> a >> b;
    55. get_primes(a);
    56. for (int i = 0; i < cnt; i ++)
    57. {
    58. int p = primes[i];
    59. sum[i] = get(a, p) - get(b, p) - get(a - b, p);
    60. }
    61. vector<int> res;
    62. res.push_back(1);
    63. for (int i = 0; i < cnt; i ++)
    64. for (int j = 0; j < sum[i]; j ++)
    65. res = mul(res, primes[i]);
    66. for (int i = res.size() - 1; i >= 0; i --) cout << res[i];
    67. cout << endl;
    68. return 0;
    69. }



    容斥原理

    基本思路:

     AcWing 890. 能被整除的数

    思路分析: 

    1.用二进制表示每种选法:1表示选当前集合,0表示不选当前集合

    2.|Sp|集合p表示1~n中p的倍数的个数,通过观察发现,|Sp| = [n | p]

    4.通过当前选中集合个数确定每一项前面的符号,再由容斥原理可以求出最终答案

     样例模拟:

    1. #include
    2. #include
    3. using namespace std;
    4. typedef long long LL;
    5. const int N = 20;
    6. int primes[N];
    7. int main()
    8. {
    9. int n, m;
    10. cin >> n >> m;
    11. for (int i = 0; i < m; i ++ ) cin >> primes[i];
    12. int res = 0;
    13. for (int i = 1; i < 1 << m; i ++)
    14. {
    15. int cnt = 0; // 记录当前集合的个数,奇数个为正,偶数个为负
    16. int t = 1; //当前选法的所有数相乘
    17. for (int j = 0; j < m; j ++ ) // 枚举每一位数, 为1则选该质数
    18. {
    19. if (i >> j & 1) //为1
    20. {
    21. if ((LL)t * primes[j] > n)
    22. {
    23. t = -1;
    24. break;
    25. }
    26. t *= primes[j]; //当前选法质数相乘
    27. cnt ++; //当前选法的质数个数
    28. }
    29. }
    30. if (t != -1)
    31. {
    32. if (cnt % 2) res += n / t; //奇数
    33. else res -= n / t; //偶数
    34. }
    35. }
    36. cout << res << endl;
    37. return 0;
    38. }



    博弈论

    Nim游戏

    公平组合游戏ICG

    (来自算法进阶指南的摘抄)
        若一个游戏满足:
        1. 由两名玩家交替行动;
        2. 在游戏进程的任意时刻,可以执行的合法行动与轮到哪名玩家无关;
        3. 不能行动的玩家判负;
        则称该游戏为一个公平组合游戏。
        NIM博弈属于公平组合游戏,但城建的棋类游戏,比如围棋,就不是公平组合游戏。因为围棋交战双方分别只能落黑子和白子,胜负判定也比较复杂,不满足条件2和条件3。

    先手必胜状态

            可以走到某个必败状态 

    先手必败状态

            走不到任何一个必败状态

    基本思路及结论:

    关于位运算中异或的相关知识参考:基础算法总结_人生导师yxc的博客-CSDN博客

    证明:

    AcWing 891. Nim游戏

    1. #include
    2. #include
    3. using namespace std;
    4. int main()
    5. {
    6. int n;
    7. cin >> n;
    8. int res = 0;
    9. while (n --)
    10. {
    11. int x;
    12. cin >> x;
    13. res ^= x;
    14. }
    15. if (res) puts("Yes");
    16. else puts("No");
    17. return 0;
    18. }

    SG函数

    mex运算:

    设S表示一个非负整数集合。定义mex(S)为求出不属于集合S的最小非负整数的运算,即:
            mex(S) = min{x}, x属于自然数,且x不属于S

    定义:

    (来自算法进阶指南)

    在有向图游戏中,对于每个节点x,设从x出发共有k条有向边,分别到达节点y1, y2, ..., yk,定义SG(x)为x的后继节点y1, y2, ..., yk 的SG函数值构成的集合再执行mex(S)运算的结果,即:
            SG(x) = mex(SG(y1), SG(y2), ..., SG(yk))
        特别地,整个有向图游戏G的SG函数值被定义为有向图游戏起点s的SG函数值,即SG(G) = SG(s)。

    AcWing 893. 集合-Nim游戏

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. const int N = 10010;
    7. int s[N], f[N];
    8. int k, n;
    9. int sg(int x)
    10. {
    11. if (f[x] != -1) return f[x];
    12. unordered_set<int> S;
    13. for (int i = 0; i < k; i ++)
    14. {
    15. int sum = s[i];
    16. if (x >= sum) S.insert(sg(x - sum));
    17. }
    18. for (int i = 0; ; i ++)
    19. {
    20. if (!S.count(i))
    21. {
    22. f[x] = i;
    23. return f[x];
    24. }
    25. }
    26. }
    27. int main()
    28. {
    29. cin >> k;
    30. for (int i = 0; i < k; i ++) cin >> s[i];
    31. memset(f, -1, sizeof f);
    32. cin >> n;
    33. int res = 0;
    34. for (int i = 0; i < n; i ++)
    35. {
    36. int x;
    37. cin >> x;
    38. res ^= sg(x);
    39. }
    40. if (res) puts("Yes");
    41. else puts("No");
    42. }

     

  • 相关阅读:
    八、【Vue-Router】编程式路由导航
    代谢组学以冬虫夏草多糖的益生机制为例研究和发现关键肠道菌群
    云原生之旅 - 13)基于 Github Action 的自动化流水线
    【SpringBoot】实现引入登录时的验证码功能
    第二十七讲.动态设置相关参数(replication为2和blocksize为10字节)
    led灯珠型号及使用参数
    Linux分区指南
    MAUI+MASA Blazor 兼容性测试报告及分析
    cubeIDE开发, stm32的ADC(模数转换器) 开发要点
    SHELL中的循环语句
  • 原文地址:https://blog.csdn.net/m0_73569492/article/details/133841389