【AtCoder】CODE FESTIVAL 2017 qual B E - Popping Balls-组合计数

版权声明:侵删,转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/81748094

传送门


题解

%%% F a c t o r i o 神犇!!!

这里介绍一种奇妙的做法,不难理解,代码也很好写。
其实并不需要考虑每种情况下 s , t 具体是多少,只需要根据当前情况需要再决定,可以证明以下情况都是存在 s , t 使之成立的:
这里设 s < t ,初始有 A 个红球, B 个蓝球可以把取球分成三个部分的动作:

  1. t 位置未变空
  2. t 位置变空, s 位置未变空
  3. s , t 均变空,此时只能从 1 位置取球

显然第 3 部分取球的序列是由前 2 部分决定且唯一的,所以不再考虑。
蓝球全部连续出现的情况太过特殊,在这里单独列出连续出现的情况数为 A + 1 。接下来讨论到的情况都是蓝球存在间隔出现的情况:
进一步把 1 , 2 过程细分:
1.1. 取出一些红球
1.2. 取出第一个蓝球,取出总共 B 个球,其中含有 i 个蓝球。
2.1. 取出一些红球
2.2. 取出第一个蓝球,取出总共 B i 个球,其中含有 j 个蓝球。
这样便枚举到了所有可能的情况,想象即使可能 t 的取值并不支持在1.2.取出 B 个球,但之后的操作也会出现同样的效果,把 t 前移是等效的。
这个不好证明,感性理解,我认真想了还是不会啊TAT。
所以统计答案只需要枚举 i , j ,再利用组合计数计算出当前 i , j 下的方案数:
首先需要满足 B i + B i j a ,方案数: ( B 1 i 1 ) ( B i 1 j 1 ) ( A ( 2 B 2 i j ) + 2 2 )
这里用到隔板法 ( B 1 i 1 ) 指把 B 个球放进 i 个箱子,每个箱子至少放一个的方案数,这里具体指1.2.中有 i 个蓝球,把 B i 个球任意放进 i 个蓝球之后的方案。同理可得, ( B i 1 j 1 ) 表示2.2.的情况。
( A ( 2 B 2 i j ) + 2 2 ) 则表示除掉1.2. 2.2.两次放入的红球外,剩余的红球分别在1.1. 2.1. 3.任意个数(某次可以为空)全部放入的方案数。
组合数预处理一下即可。


代码

#include<bits/stdc++.h>
using namespace std;

const int mod=1e9+7,N=2050;
int a,b,c[N][N],ans;

inline int ad(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}
inline int mul(int x,int y){return 1ll*x*y%mod;}

int main(){
    int i,j;
    c[0][0]=1;
    for(i=1;i<N;++i){
        c[i][0]=c[i][i]=1;
        for(j=1;j<i;++j) c[i][j]=ad(c[i-1][j-1],c[i-1][j]);
    }
    scanf("%d%d",&a,&b);
    ans=a+1;
    for(i=max(1,b-a);i<b;++i)
        for(j=max(1,2*(b-i)-a);j<=b-i;++j)
            ans=ad(ans,mul(mul(c[b-1][i-1],c[b-i-1][j-1]),c[a-2*(b-i)+j+2][2]));    
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/81748094