题目链接

总结:组合数

这$F$题好难啊...只会部分分做法,下面两个方法都是部分分做法。满分做法我去看看...会的话就补一下

部分分做法

方法1:

首先$A$能赢的条件很明显,假设在所有的牌里面取出$A$张$A$牌,$B$张$B$牌,$C$张$C$牌,那么$A$能赢当且仅当$A=n,B<m,C<k$

所以假设我们在拿出了$n$张$A$牌的情况下,中间穿插着拿了$B$张$B$牌,$C$张$C$牌,则有

$$\sum_{i=0}^{i<=m+k}C(n-1,i+n-1)*3^{m+k-i}*\sum_{j=0}^{j<=m,j<=k}C(i,j)$$

首先在$i+n-1$张牌中取$n-1$张$A$牌的方案为$C(n-1,n+i-1)$

注意:最后一张牌一定需要是$A$,所以就只能有$n-1$

而且剩下的牌数的排列为$3^{m+k-i}$,要乘上去

以及在$i+n-1$中$B$和$C$的排列为$C(i,j)$,也要乘起来

所以就得到了上面的公式

但是,上面的做法我打挂了...这个式子是对的,我算后面那个$\sum_{j=0}^{j<=m,j<=k}C(i,j)$那里我没有枚举好...查不出来啊...

我在理解了满分做法之后终于发现我挂在哪里了...

在枚举后面的东西的时候我没有分类讨论。但是改完后貌似还分少了一类...

$subtask_1$10个点我错了$2$个...然后$subtask_2$被我强行水过去$2$个点..

用$C$牌数量来分类:分为$j<k$,$j<m$,$j<i$(第三类我没分...不想去改了...)

对于第一种情况:$x=\sum_{j=0}^{j<k}C(i,j)$

对于第二种情况:$x=\sum_{j=0}^{j<m}C(i,k)$

对于第三种情况:请读者独立思考(知道有第三种情况是因为我去看了一下满分的做法...)

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 5010
  6. #define ll long long
  7. const int mod = 1e9 + ;
  8.  
  9. int n , m , k ;
  10. ll fac[ N ] , ifac[ N ] ;
  11.  
  12. ll power( ll a ,ll b ) {
  13. ll base = a , ans = ;
  14. while( b ) {
  15. if( b& ) ans = ( ans * base ) % mod ;
  16. base = ( base * base ) % mod ;
  17. b >>= ;
  18. }
  19. return ans ;
  20. }
  21. ll mul( ll x ,ll y ) {
  22. return ( 1ll * x * y ) % mod ;
  23. }
  24.  
  25. ll inv( ll x ) {
  26. return power( x , mod - ) % mod ;
  27. }
  28.  
  29. int main() {
  30. scanf( "%d%d%d" , &n , &m , &k ) ;
  31.  
  32. fac[ ] = ;
  33. for( int i = ; i < N ; i ++ ) fac[ i ] = mul( fac[ i - ] , i ) ;
  34. for( int i = ; i < N ; i ++ ) ifac[ i ] = inv( fac[ i ] ) ;
  35.  
  36. ll ans = , sum = n + k + m ;
  37.  
  38. for( int i = n ; i <= sum; i ++ ) {// 取出n张A牌
  39. for( int j = ; j <= m ;j ++ ) {//B牌数量
  40. int C = i - n - j ;//C牌数量
  41. if( C < || C > k ) continue ;
  42. ll tmp = mul( fac[ i - ] , mul( ifac[ n - ] , mul( ifac[ j ] ,ifac[ C ] ) ) ) ;
  43. tmp = mul( tmp , power( , sum - i ) ) ;
  44. ans = ( ans + tmp ) % mod ;
  45. }
  46. }
  47.  
  48. printf( "%lld\n" , ans ) ;
  49.  
  50. }

部分分做法1(有一定错误...)

方法2:

换一种想法,在前$i$张卡片中拿出$n$张$A$,$j$张$B$,$C$张$C$

则可以推出一个公式

$$\sum_{i=0}^{i<=n+m+k}\frac{(i-1)!}{(n-1)!j!C!}3^{n+k+m-i}$$

如果看得懂那个方法一的话这个公式大概也是看得懂的吧...

就是$i-1$的全排列数除掉$n-1$的全排列数和$j$的全排列数和$C$的全排列数

这个是很基础的一个组合数的常识,这样子得到的就是我们要的当前情况的方案数,记住也要把当前剩下的那些的方案数也乘上去,即$3^{n+m+k-i}$

然后这里的除法是$mod$ $m$意义下的,所以要求一下逆元,代码中的$fac[i]$即为$i!$的值,$ifac[i]$即为$i!$的逆元

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. #define N 5010
  6. #define ll long long
  7. const int mod = 1e9 + ;
  8.  
  9. int n , m , k ;
  10. ll fac[ N ] , ifac[ N ] ;
  11.  
  12. ll power( ll a ,ll b ) {
  13. ll base = a , ans = ;
  14. while( b ) {
  15. if( b& ) ans = ( ans * base ) % mod ;
  16. base = ( base * base ) % mod ;
  17. b >>= ;
  18. }
  19. return ans ;
  20. }
  21. ll mul( ll x ,ll y ) {
  22. return ( 1ll * x * y ) % mod ;
  23. }
  24.  
  25. ll inv( ll x ) {
  26. return power( x , mod - ) % mod ;
  27. }
  28.  
  29. int main() {
  30. scanf( "%d%d%d" , &n , &m , &k ) ;
  31.  
  32. fac[ ] = ;
  33. for( int i = ; i < N ; i ++ ) fac[ i ] = mul( fac[ i - ] , i ) ;
  34. for( int i = ; i < N ; i ++ ) ifac[ i ] = inv( fac[ i ] ) ;
  35.  
  36. ll ans = , sum = n + k + m ;
  37.  
  38. for( int i = n ; i <= sum; i ++ ) {// 取出n张A牌
  39. for( int j = ; j <= m ;j ++ ) {//B牌数量
  40. int C = i - n - j ;//C牌数量
  41. if( C < || C > k ) continue ;
  42. ll tmp = mul( fac[ i - ] , mul( ifac[ n - ] , mul( ifac[ j ] ,ifac[ C ] ) ) ) ;
  43. tmp = mul( tmp , power( , sum - i ) ) ;
  44. ans = ( ans + tmp ) % mod ;
  45. }
  46. }
  47.  
  48. printf( "%lld\n" , ans ) ;
  49.  
  50. }

部分分做法2

钻研了很久的题解,终于差不多搞懂满分做法了...

满分做法

方法一确实是对的。这个满分做法就是用来改进那里的

观察耗时,耗时基本都是花费在枚举后面那一段,所以考虑优化那一段...(这个做法很清奇...正常写组合数不应该是优化那个式子吗...)

$$\sum_{i=0}^{i<=m+k}C(n-1,i+n-1)*3^{m+k-i}*\sum_{j=0}^{j<=m,j<=k}C(i,j)$$

还是这个式子,我们来优化掉后面那个$\sum_{j=0}^{j<=m,j<=k}C(i,j)$(就是我写错了还查不出来的那个玩意...不!我写到这里的时候忽然发现我在枚举的时候没有分类讨论!)

我们把后面那个$\sum$拆掉,分三部分讨论

假设我们现在手上有$x$张$C$牌

当$x<k$时,随便取...(C的数量都比你要取的多了所以肯定不会超限)即$\sum_{i=0}^{x<k}C(x,i)$

当$x<m$时,$C$的数量在$i$以内,同时不能取超过$k$个,即$\sum_{i=0}^{i<m}C(x,i)$

当$m<=x<m+k$时,$B,C$数量均不超过$i$,同时$B$不能取超过$m$个,$C$不能取超过$k$个,即$\sum_{i-m+1}^{i<k}C(x,i)$

然后怎么优化呢

如果你熟知杨辉三角这个东西的话,大概就会知道怎么优化了

假设我们已经知道了$i-1$时$x$的值,那么其实可以推出下面的几个式子:

情况$1$:$x=x*2$

情况$2$:$x=x*2-C(i,k)$

情况$3$:$x-x*2-C(i,k)-C(i,m)$

这个挺容易推的吧...如果部分分做法的第一种有推出来那么这个优化也就顺理成章了的样子(但是我推出来了一半...)

  1. #include <bits/stdc++.h>
  2.  
  3. using namespace std ;
  4.  
  5. const int mod = 1e9 + ;
  6. const int N = ;
  7. #define ll long long
  8.  
  9. int n , m , k ;
  10. ll fac[ N ] , ifac[ N ] , p[ N ] ;
  11.  
  12. ll mul( ll x , ll y ) {
  13. return ( 1ll * x * y ) % mod ;
  14. }
  15.  
  16. ll add( ll x , ll y ) {
  17. return ( x + y ) % mod ;
  18. }
  19.  
  20. ll power( ll a , ll b ) {
  21. int ans = , base = a ;
  22. while( b ) {
  23. if( b& ) ans = mul( ans , base ) ;
  24. base = mul( base , base ) ;
  25. b >>= ;
  26. }
  27. return ans ;
  28. }
  29.  
  30. ll inv( ll x ) {
  31. return power( x , mod - ) % mod ;
  32. }
  33.  
  34. ll C( ll x , ll y ) {
  35. return ( fac[ x ] * ifac[ y ] % mod * ifac[ x - y ] % mod ) % mod ;
  36. }
  37.  
  38. int main() {
  39. scanf( "%d%d%d" , &n , &m , &k ) ;
  40. fac[ ] = 1ll ;
  41. p[ ] = 1ll ;
  42. for( int i = ; i < N ; i ++ ) {
  43. fac[ i ] = fac[ i - ] * i % mod ;
  44. p[ i ] = p[ i - ] * 3ll % mod ;
  45. }
  46. for( int i = ; i < N ; i ++ ) {
  47. ifac[ i ] = inv( fac[ i ] ) ;
  48. }
  49. ll ans = , x = 1ll ;
  50. n -- ;
  51. if( m < k ) swap( m , k ) ;
  52. for( int i = ; i <= m + k ; i ++ ) {
  53. ans = ( ans + C( n + i , n ) * p[ m + k - i ] % mod * x ) % mod ;
  54. /*
  55. for( int j = 0 ; j <= min( i , m ) ; j ++ ) {
  56. if( i - j > k || i - j < 0 ) break ;
  57. x = add( x , C( i , j ) ) ;
  58. }
  59. */
  60. if( i < k ) x = ( x * 2ll ) % mod ;
  61. else if( i < m ) x = ( x * 2ll - C( i , k ) ) % mod ;
  62. else x = ( x * 2ll - C( i , k ) - C( i , m ) ) % mod ;
  63. }
  64. printf( "%lld\n" , add( ans , mod ) ) ;
  65. return ;
  66. }

数学题真的虐哭我...这题我研究了$3$天...

不过确实是一道组合数的好题...做完后觉得对组合数这玩意的理解更深了一些...

[AtCoder ARC061F]Card Game for Three 组合数好题的更多相关文章

  1. HDU 6114 Chess【逆元+组合数】(组合数模板题)

    <题目链接> 题目大意: 車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋子阻隔的棋子.一天,小度在棋盘上摆起了许多車……他想知道,在一共N×M个点的矩形棋盘中摆最多个数的車使 ...

  2. AtCoder Beginner Contest 022 A.Best Body 水题

    Best Body Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://abc022.contest.atcoder.jp/tasks/abc02 ...

  3. 【atcoder】Two Sequences [arc092 D](思维题)

    题目传送门:https://arc092.contest.atcoder.jp/tasks/arc092_b 这场arc好难啊...这场感觉不像正常的arc...其实这道题还可以更早写出来的,但是蒟蒻 ...

  4. Atcoder Tenka1 Programmer Contest D: IntegerotS 【思维题,位运算】

    http://tenka1-2017.contest.atcoder.jp/tasks/tenka1_2017_d 给定N,K和A1...AN,B1...BN,选取若干个Ai使它们的或运算值小于等于K ...

  5. Atcoder CODE FESTIVAL 2016 qual C 的E题 Encyclopedia of Permutations

    题意: 对于一个长度为n的排列P,如果P在所有长度为n的排列中,按照字典序排列后,在第s位,则P的value为s 现在给出一个长度为n的排列P,P有一些位置确定了,另外一些位置为0,表示不确定. 现在 ...

  6. AtCoder 杂题训练

    前言: 因为要普及了,今年没一等就可以退役去学文化课了,所以暑假把历年noip普及组都刷了一遍,离noip还有50+天,想弄点强化训练什么的. 想了想,就这些天学文化课之余有空就把AtCoder之前那 ...

  7. SDUT1061Binomial Showdown(组合数)

    http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=1061 题意 : 表示这个题的英文没看懂,就看懂 ...

  8. Iroha and a Grid AtCoder - 1974(思维水题)

    就是一个组合数水题 偷个图 去掉阴影部分  把整个图看成上下两个矩形 对于上面的矩形求出起点到每个绿点的方案 对于下面的矩形 求出每个绿点到终点的方案 上下两个绿点的方案相乘后相加 就是了 想想为什么 ...

  9. poj1850(组合数)

    题目链接:http://poj.org/problem;jsessionid=B0D9A01EC0F1043088A37454B6CED469?id=1850 题意:给字符串编号,该字符串必须满足由小 ...

随机推荐

  1. /etc/issue 查看系统版本号

    查看系统版本号 [root@mysql bin]# cat /etc/issue CentOS release 6.4 (Final) Kernel \r on an \m

  2. dedecms如何调用当前栏目的子栏目及子栏目文章

    前面ytkah谈到了 dedecms调用当前栏目的子栏目怎么操作,有的朋友会问如果再增加一个调用子栏目文章的需求,即调用当前栏目的子栏目及子栏目文章,这个有办法实现吗?这时就要涉及到另外两个标签的调用 ...

  3. js-jquery-SweetAlert2【一】使用

    概述:SweetAlert2是SweetAlert-js的升级版本,它解决了SweetAlert-js中不能嵌入HTML标签的问题,并对弹出对话框进行了优化,同时提供对各种表单元素的支持,还增加了5种 ...

  4. 发现《深入理解C++11》中扩展的friend代码的错误

    目前在总结现代C++的新特性,看了<深入理解C++11>这本书. 今天看到扩展的friend语法这一节,遇到了问题.本节电子版内容参见:https://book.2cto.com/2013 ...

  5. ubuntu系统下怎么安装gcc编译器

    你安装一个名字叫做build-essential的软件包,就可以一次将编译器.make工具.所有的编程头文件.函数库等东东全部安装上,其中也包括gcc编译器,这是非常稳妥的安装方式,安装命令是用roo ...

  6. python 多线程小练习

    需求:有100个数据,启动5个线程,每个线程分20个数据,怎么把这20个数据分别传给每个线程. 1. 利用多线程实现 import threading nums = list(range(100)) ...

  7. mysql查询表基本操作

    数据库表的创建create table <表名>( <列名> <数据类型及长度> [not null], <列名> <数据类型及长度>, . ...

  8. NPOI 导出excel 分表

    /// <summary> /// 由DataTable导出Excel[超出65536自动分表] /// </summary> /// <param name=" ...

  9. MySQL从删库到跑路_高级(一)——数据完整性

    作者:天山老妖S 链接:http://blog.51cto.com/9291927 一.数据完整性简介 1.数据完整性简介 数据冗余是指数据库中存在一些重复的数据,数据完整性是指数据库中的数据能够正确 ...

  10. 数据仓库基础(二)ETL

    本文转载自:http://www.cnblogs.com/evencao/archive/2013/06/14/3135529.html ETL在数据仓库中具有以下的几个特点: 数据流动具有周期性: ...