[关闭]
@913094331 2017-04-22T08:46:18.000000Z 字数 2497 阅读 971

萌新集中营第四期——数学小知识

题解


题目链接:https://vjudge.net/contest/154954

A - Eddy's AC难题

题目大意

从 n 个人中选择一部分人(或者全部)分成两组进行比较 ac 的数量,要求使第一组中的最小 ac 数大于第二组中的最大 ac 数,求出情况数,假设摘录下的人数为 n 人,而且每个人 ac 的数量不会相等,最后结果在64位整数范围内

Input

包含多组数据,每组包含一个整数 n

Output

输出符合要求的总的方案数,每个输出占一行

Sample Input

2
4

Sample Output

1
17

解题思路

**组合数**
    插板法,用插板来分组,answer = 1*C(2, n) + 2*C(3, n) + …… + (n-1)*C(n, n),从 n 个人中依次抽取2,3,……,n个人,插板的位置依次有1,2,……,n-1种情况

代码

  1. #include <cstdio>
  2. #define ll long long int
  3. ll Combination(ll n, ll m);
  4. int main()
  5. {
  6. ll n;
  7. while(scanf("%lld", &n)!=EOF)
  8. {
  9. ll ans = 0, i;
  10. for(i=2;i<=n;i++)
  11. {
  12. ans = ans + (i-1)*Combination(n, i);
  13. }
  14. printf("%lld\n", ans);
  15. }
  16. return 0;
  17. }
  18. ll Combination(ll n, ll m)
  19. {
  20. ll i, j, c = 1;
  21. for(i=n, j=1;i>n-m;i--,j++)
  22. {
  23. c = c * i / j;
  24. }
  25. return c;
  26. }

该题收获

1. 刚开始混淆了组合数和排列数,本题需要利用插板分割和组合数

B - RPG的错排

题目大意

将 n 个人和她们的名字一一对应,要求答对一半或以上才算过关,请问有多少组情况能顺利过关

Input

多个 case ,每个 case 包括一个 n (n<=25), 当 n = 0 时输入结束

Output

输出情况数,每个输出占一行

Sample Input

1
2
0

Sample Output

1
1

解题思路

**错排**
    将错排和组合数相结合, n 个人中取二分之一,D(0)*C(n,n-0)+D(1)*C(n,n-1)+……+D(n/2)*C(n,n-n/2)即为所求结果

代码

  1. #include <cstdio>
  2. #define e 2.718281828459
  3. typedef long long int ll;
  4. ll Combination(ll n, ll m);
  5. int main()
  6. {
  7. ll n;
  8. while(scanf("%lld", &n)&&n!=0)
  9. {
  10. ll i, j, ans=1, d;
  11. for(i=1;i<=n/2;i++)
  12. {
  13. for(j=1,d=1;j<=i;j++)
  14. {
  15. d = d * j;
  16. }
  17. ans = ans + (long long int)((long double)d/e+0.5) * Combination(n, n-i);
  18. }
  19. printf("%lld\n", ans);
  20. }
  21. return 0;
  22. }
  23. ll Combination(ll n, ll m)
  24. {
  25. ll i, j, c = 1;
  26. for(i=n, j=1;i>n-m;i--,j++)
  27. {
  28. c = c * i / j;
  29. }
  30. return c;
  31. }

该题收获

1. 错排公式:D(n) = n! [(-1)^2/2! + … + (-1)^(n-1)/(n-1)! + (-1)^n/n!]
   简化后的公式:D(n) = [n!/e+0.5]   (e = 2.718281828459,这里[x]为x的整数部分)
2. 错排公式可由递归或容斥原理推导而来,理解了两种来源方式
   递归公式:D(n) = (n-1) [D(n-2) + D(n-1)]
3. 除了应用在结构体上, typedef 的使用,效果与 #define 相似

G - The Euler function

题目大意

欧拉函数 euler(n) 是指在 [1, n) 中与 n 互质的数的个数,求出 [euler(a), euler(b)] 的总和

Input

包含多组用例,每行有两个整数 a , b (2

Output

输出 euler(a) + euler(a+1) + …… + euler(b) 的结果

Sample Input

3 100

Sample Output

3042

解题思路

**欧拉函数**
    当数据过大,直接求解会超时,需要用筛选法打表

代码

  1. #include <cstdio>
  2. #define Max 3000001
  3. typedef long long int LL;
  4. void Init();
  5. LL euler[Max];
  6. int main()
  7. {
  8. LL a, b, ans, i;
  9. Init();
  10. while(scanf("%lld %lld", &a, &b)!=EOF)
  11. {
  12. ans = 0;
  13. for(i=a;i<=b;i++)
  14. {
  15. ans = ans + euler[i];
  16. }
  17. printf("%lld\n", ans);
  18. }
  19. return 0;
  20. }
  21. void Init()
  22. {
  23. euler[1]=1;
  24. for(int i=2;i<Max;i++)
  25. euler[i]=i;
  26. for(int i=2;i<Max;i++)
  27. if(euler[i]==i)
  28. for(int j=i;j<Max;j+=i)
  29. euler[j]=euler[j]/i*(i-1);
  30. }

该题收获

1. 定义的数组长度不能过大,会编译错误,或者超出内存
2. 求欧拉函数有两种办法,注意当数据过大时直接求解会超时,需要筛选法打欧拉函数表
3. 理解了欧拉函数直接求解的方式
4. 先进行除法防止中间数据溢出
5. euler(1) = 1
6. 欧拉公式的延伸:一个数的所有质因子之和是euler(n)*n/2

@欧拉函数模板

  1. //直接求解欧拉函数
  2. int euler(int n)
  3. {
  4. int res=n,a=n;
  5. for(int i=2;i*i<=a;i++)
  6. {
  7. if(a%i==0)
  8. {
  9. res=res/i*(i-1);
  10. while(a%i==0) a/=i;
  11. }
  12. }
  13. if(a>1) res=res/a*(a-1);
  14. return res;
  15. }
  16. //筛选法打欧拉函数表
  17. #define Max 1000001
  18. int euler[Max];
  19. void Init()
  20. {
  21. euler[1]=1;
  22. for(int i=2;i<Max;i++)
  23. euler[i]=i;
  24. for(int i=2;i<Max;i++)
  25. if(euler[i]==i)
  26. for(int j=i;j<Max;j+=i)
  27. euler[j]=euler[j]/i*(i-1);
  28. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注