Atcoder AGC002 F : Leftmost Ball(DP)

传送门

题解:
很妙的一道题。

考虑怎么统计得不重不漏,每一种方案的除了白点的每一种颜色第一个出现的位置一定不同而且有顺序, 我们可以强制这个顺序为 1n 1 → n , 最后乘上 n! n ! 。 同时,我们可以把每种方案的白点按照这个顺序赋颜色 (因为每一种方案这样赋颜色肯定合法,而且每一种合法的方案这样染色回去一定对应一种原始方案,所以他们的关系是双射)。

现在问题变为了:给定 n n 个球,每种颜色的第一个球出现有顺序,第二个球出现也有顺序,且每种颜色的第一个球必须在第二个球之前,这个问题可以形象的表示为下面这个图的Top序个数:
这里写图片描述

我们知道求解一般 DAG D A G Top T o p 序个数是 NPcompletive N P − c o m p l e t i v e 的,不过这个 DAG D A G 图我们很好构造 DP D P 状态: fi,j f i , j 表示下面的点位于第 i i 列,上面的点位于第j列的 Top T o p 序数量,显然转移只有两种: fi1,jfi,j,fi,j1fi,j f i − 1 , j → f i , j , f i , j − 1 → f i , j .具体的 :
fi,j=fi,j1+fi1,j((k2)+((i1)k(i1j))k2)fi,j10i>ji=ji>j f i , j = { f i , j − 1 + f i − 1 , j ∗ ( ( k − 2 ) + ( ( i − 1 ) ∗ k − ( i − 1 − j ) ) k − 2 ) i > j f i , j − 1 i = j 0 i > j

组合数在含义是两边的 Top T o p 序列无关,可以任意结合。

#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
const int mod=1e9+7,N=2e3+50;
inline int add(int x,int y) {return (x+y>=mod)?(x+y-mod):(x+y);}
inline int mul(int x,int y) {return (LL)x*y%mod;}
inline int power(int a,int b) {
    int rs=1;
    for(;b;b>>=1,a=mul(a,a)) if(b&1) rs=mul(rs,a);
    return rs;
}
int n,k,dp[N][N],fac[N*N*2],ifac[N*N*2];
inline int C(int x,int y) {return mul(ifac[x-y],mul(fac[x],ifac[y]));}
int main() {
    cin>>n>>k; int l=2*n*k;
    if(k==1) {puts("1"); return 0;}
    fac[0]=1;
    for(int i=1;i<=l;i++) fac[i]=mul(fac[i-1],i);
    ifac[l]=power(fac[l],mod-2);
    for(int i=l-1;~i;i--) ifac[i]=mul(ifac[i+1],i+1);
    dp[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=i;j++) {
            if(j==i) dp[i][j]=dp[i][j-1];
            else dp[i][j]=add(dp[i][j-1],mul(dp[i-1][j],C(k-2+(i-1)*k-(i-j-1),k-2)));
        }
    printf("%d\n",mul(dp[n][n],fac[n]));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值