• 【数论:组合数学】排列组合


    知识点

    一 . 加法原理与乘法原理

    加法原理:分类思想。一个事件的发生,分为几类事件的发生,通俗的说是好几种情况的发生,通常几类事情之间不会相互影响。

    乘法原理:分步思想。一个事件的发生,分为几个子事件分步发生,即每个子事件按顺序发生,子事件是独立的,内部发生的概率一样。
     


    一 . 加法原理与乘法原理

    (一)加法原理

    (二)乘法原理

    例题:洛谷 P3197 [HNOI2008]越狱

    思路:在当前条件下,能导致越狱的情况很多,但是不能越狱的情况,只需保证相邻的两个房间的犯人宗教不同即可。总情况减去不能越狱的情况就是能导致越狱的情况。

     在不考虑能否越狱的情况下,有n个房间,m种宗教,总共有m^n种情况。保证不能越狱的条件下,第一个房间的犯人可以有m种宗教的选择,后面 n-1 个房间的犯人只需保证与前一个房间的宗教不同即可,有 m-1 种情况,总共有 m*(m-1)^{n-1} 种情况。那么最后得到可能发生越狱的情况总数为 m^n-m*(m-1)^{n-1} 种。最后,在此题中,指数增长会导致循环超时,需要进行快速幂优化。

    未优化代码(30分):

    1. #include
    2. #include
    3. #include
    4. #include
    5. #define ll long long
    6. #define re register
    7. using namespace std;
    8. const int mod=100003;
    9. ll int n,m,res1=1; //res1用于统计总情况数
    10. inline ll int read()
    11. {
    12. ll int x=0,f=1;
    13. char c=getchar();
    14. while(c<'0'||c>'9')
    15. {
    16. if(c=='-') f=-1;
    17. c=getchar();
    18. }
    19. while(c>='0'&&c<='9')
    20. {
    21. x=(x<<3)+(x<<1)+(c^48);
    22. c=getchar();
    23. }
    24. return x*f;
    25. }
    26. inline void write(ll int x)
    27. {
    28. if(x>9) write(x/10);
    29. putchar(x%10+'0');
    30. }
    31. int main()
    32. {
    33. m=read(); n=read();
    34. for(re ll int i=1;i<=n;++i)
    35. {
    36. res1=(res1*m)%mod;
    37. }
    38. ll int ans=m-1,res2=m; //res2用于统计不能越狱的情况
    39. for(re ll int i=1;i<=n-1;++i)
    40. {
    41. res2=(res2*ans)%mod;
    42. }
    43. write(res1-res2);
    44. return 0;
    45. }

    优化代码:(使用快速幂进行优化)

    1. #include
    2. #include
    3. #include
    4. #include
    5. #define ll long long
    6. #define re register
    7. using namespace std;
    8. const int mod=100003;
    9. ll int n,m,res1=1,res3;
    10. inline ll int read()
    11. {
    12. ll int x=0,f=1;
    13. char c=getchar();
    14. while(c<'0'||c>'9')
    15. {
    16. if(c=='-') f=-1;
    17. c=getchar();
    18. }
    19. while(c>='0'&&c<='9')
    20. {
    21. x=(x<<3)+(x<<1)+(c^48);
    22. c=getchar();
    23. }
    24. return x*f;
    25. }
    26. inline void write(ll int x)
    27. {
    28. if(x>9) write(x/10);
    29. putchar(x%10+'0');
    30. }
    31. inline ll int poww(ll int x,ll int y)
    32. {
    33. ll int ans=1;
    34. if(y==0) ans=1;
    35. while(y>0)
    36. {
    37. if(y & 1)
    38. {
    39. ans=(ans*x)%mod;
    40. }
    41. x=x*x%mod;
    42. y=y>>1;
    43. }
    44. return ans%mod;
    45. }
    46. int main()
    47. {
    48. m=read(); n=read();
    49. res1=poww(m,n)%mod;
    50. ll int ans=m-1,res2=m;
    51. res2=res2*poww(m-1,n-1)%mod;
    52. res3=res1-res2;
    53. if(res3<0) res3+=mod;
    54. //可能会出现总情况数取模后的数小于不能越狱情况下取模后的数,结果会为负数
    55. write(res3);
    56. return 0;
    57. }

    二 . 组合数的应用

    1 . 组合数与杨辉三角

    例题: 洛谷 P2822 [NOIP2016 提高组] 组合数问题

     (待填坑)

    1. #include
    2. #include
    3. #include
    4. #include
    5. #define ll long long
    6. #define re register
    7. using namespace std;
    8. ll int t,k,m,n;
    9. const int maxn=2e3+5;
    10. ll int c[maxn][maxn],sum[maxn][maxn];
    11. inline ll int read()
    12. {
    13. ll int x=0,f=1;
    14. char c=getchar();
    15. while(c<'0'||c>'9')
    16. {
    17. if(c=='-') f=-1;
    18. c=getchar();
    19. }
    20. while(c>='0'&&c<='9')
    21. {
    22. x=(x<<3)+(x<<1)+(c^48);
    23. c=getchar();
    24. }
    25. return x*f;
    26. }
    27. int main()
    28. {
    29. t=read(); k=read();
    30. c[0][0]=c[1][1]=1; //在进行查找前进行数据存储
    31. for(re ll int i=1;i<=2000;++i)
    32. {
    33. c[i][0]=1;
    34. for(re ll int j=1;j<=2000;++j)
    35. {
    36. c[i][j]=(c[i-1][j]%k+c[i-1][j-1]%k)%k;
    37. sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
    38. if(c[i][j]==0 && i>=j)
    39. {
    40. sum[i][j]++;
    41. }
    42. }
    43. sum[i][i+1]=sum[i][i];
    44. }
    45. while(t--)
    46. {
    47. n=read(); m=read();
    48. if(m>n) printf("%lld\n",sum[n][n]);
    49. else printf("%lld\n",sum[n][m]);
    50. }
    51. return 0;
    52. }

    2 . 计算平面图形的对角线

    例题: 洛谷 P2181 对角线

    题目描述

    对于一个 n 个顶点的凸多边形,它的任何三条对角线都不会交于一点。请求出图形中对角线交点的个数。

    例如,6 边形:

    输入格式

    输入只有一行一个整数 n,代表边数。

    输出格式

    输出一行一个整数代表答案。

    输入输出样例

    输入 #1

    3

    输出 #1

    0

    输入 #2

    6

    输出 #2

    15

    说明/提示

    数据规模与约定

    • 对于 50% 的数据,保证 3 \leqslant n \leqslant 100
    • 对于 100% 的数据,保证 3 \leqslant n \leqslant 10^5

     思路:

    对于一个多边形来说,任意选不同的四个点就可以产生一个由于对角线相交而产生的交点,也可以看做是一个四边形的两条对角线交于一点,求四边形的数量。因此可以得到式子:C_{n}^{4} = \frac{n*(n-1)*(n-2)*(n-3)}{4!}(注:4!=4*3*2*1)。需要注意的是:数据范围特别大,可能会超过long long,这里采用unsigned long long。

    1. #include
    2. #include
    3. #include
    4. #include
    5. #define ll unsigned long long
    6. using namespace std;
    7. ll int n;
    8. inline ll int read()
    9. {
    10. ll int x=0,f=1;
    11. char c=getchar();
    12. while(c<'0'||c>'9')
    13. {
    14. if(c=='-') f=-1;
    15. c=getchar();
    16. }
    17. while(c>='0'&&c<='9')
    18. {
    19. x=(x<<3)+(x<<1)+(c^48);
    20. c=getchar();
    21. }
    22. return x*f;
    23. }
    24. inline void write(ll int x)
    25. {
    26. if(x>9) write(x/10);
    27. putchar(x%10+'0');
    28. }
    29. int main()
    30. {
    31. n=read();
    32. ll int res=n*(n-1)/2*(n-2)/3*(n-3)/4;
    33. //几个数相乘以后就除以几,可以保证数据不会增长得太大导致结果错误
    34. write(res);
    35. return 0;
    36. }

     

  • 相关阅读:
    HTTP长连接实现原理
    EF Core + MySQL 基本增删改查
    十天学完基础数据结构-第四天(链表(Linked List))
    基于R语言平台Biomod2模型的物种分布建模与可视化分析
    数据结构与算法系列二之链表、哈希表及栈
    业务线程池阻塞分析
    考研:研究生考试(五天学完)之《线性代数与空间解析几何》研究生学霸重点知识点总结之第一课行列式
    LeetCode 442 数组中重复的数据
    mermaid - markdown的mermaid语法解析渲染
    【调制解调】DSB 双边带调幅
  • 原文地址:https://blog.csdn.net/gzkeylucky/article/details/126589287