[AGC002F]Leftmost Ball
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[AGC002F]Leftmost Ball相关的知识,希望对你有一定的参考价值。
Leftmost Ball
题解
首先一个序列白化后合法必须要求前缀白球数不小于前缀颜色数,否则肯定有个白球染的不是该种颜色的第一个球。
所以显然我们可以将球分成白球与有颜色的球往里放,有颜色的球重要的是第一个放置(未被染白的)的位置,这个位置会影响前缀颜色数,确定了第一个位置,其它球在后面乱填就好了,而每一个白球的位置都很重要,都会影响前缀和。
所以我们可以想到依次确定当前的第一个空位填那个球,记
d
p
i
,
j
dp_i,j
dpi,j表示已经填了
i
i
i个白球,
j
j
j组有颜色球的方案数,显然转移就是下一个填白球还是有颜色的球,有转移方程式,
d
p
i
,
j
=
d
p
i
−
1
,
j
+
(
n
k
−
i
−
(
j
−
1
)
(
k
−
1
)
−
1
k
−
2
)
d
p
i
,
j
−
1
dp_i,j=dp_i-1,j+\\binomnk-i-(j-1)(k-1)-1k-2dp_i,j-1
dpi,j=dpi−1,j+(k−2nk−i−(j−1)(k−1)−1)dpi,j−1白球显然是不需要区分颜色的,而后面有颜色的球可以先不考虑颜色,只要保证填的数量比白球少,将在后面的序列放置的方案数,也就是那个组合数乘上转移就行。最后再乘上
n
!
n!
n!表示有颜色的球第一个出现的颜色序列就行,毕竟不同颜色间是等价的。
顺着
d
p
dp
dp一次就可以了。
时间复杂度
O
(
n
k
)
O\\left(nk\\right)
O(nk)。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2005
#define MAXM 4000005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=1e9+7;
const int mod=1e5+3;
const int inv2=5e8+4;
const int jzm=2333;
const int zero=2000;
const int n1=1000;
const int M=100000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x)return x<0?-x:x;
template<typename _T>
void read(_T &x)
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0')if(s=='-')f=-1;s=getchar();
while('0'<=s&&s<='9')x=(x<<3)+(x<<1)+(s^48);s=getchar();
x*=f;
template<typename _T>
void print(_T x)if(x<0)x=(~x)+1;putchar('-');if(x>9)print(x/10);putchar(x%10+'0');
int gcd(int a,int b)return !b?a:gcd(b,a%b);
int add(int x,int y,int p)return x+y<p?x+y:x+y-p;
void Add(int &x,int y,int p)x=add(x,y,p);
int qkpow(int a,int s,int p)int t=1;while(s)if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;return t;
int n,K,dp[MAXN][MAXN],fac[MAXM],inv[MAXM],ff[MAXM];
void init()
fac[0]=fac[1]=inv[0]=inv[1]=ff[1]=1;
for(int i=2;i<=n*K;i++)
fac[i]=1ll*i*fac[i-1]%mo,
ff[i]=1ll*(mo-mo/i)*ff[mo%i]%mo,
inv[i]=1ll*ff[i]*inv[i-1]%mo;
int C(int x,int y)
if(x<0||y<0||x<y)return 0;
return 1ll*fac[x]*inv[y]%mo*inv[x-y]%mo;
signed main()
read(n);read(K);init();dp[0][0]=1;
if(K==1)puts("1");return 0;
for(int i=1;i<=n;i++)
for(int j=0;j<=i;j++)
if(j<i)Add(dp[i][j],dp[i-1][j],mo);
if(j>0)Add(dp[i][j],1ll*C(n*K-i-(j-1)*(K-1)-1,K-2)*dp[i][j-1]%mo,mo);
printf("%lld\\n",1ll*fac[n]*dp[n][n]%mo);
return 0;
谢谢!!!
以上是关于[AGC002F]Leftmost Ball的主要内容,如果未能解决你的问题,请参考以下文章
AtCoder Grand Contest 002 (AGC002) F - Leftmost Ball 动态规划 排列组合