一、题目
题意:
求多重集
{
a
1
×
s
1
,
.
.
.
,
a
k
×
s
k
}
\{a1\times s1,...,ak\times sk\}
{a1×s1,...,ak×sk}的一排列数+二排列数+……+
n
n
n排列数。
数据范围:
k
,
s
k
≤
100
k,sk\leq 100
k,sk≤100
二、解法
0x01 前置芝士
多重集有
k
k
k种元素,每种元素的个数是无限(多个)的。
0x02 dp
设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前
i
i
i种元素一共选了
j
j
j个,对于第
i
i
i种元素有两种情况:
1、不选 即
f
[
i
+
1
]
[
j
]
=
f
[
i
]
[
j
]
f[i+1][j]=f[i][j]
f[i+1][j]=f[i][j]
2、选
t
t
t个,那么
f
[
i
+
1
]
[
j
]
=
f
[
i
]
[
j
−
t
]
∗
C
(
j
,
t
)
f[i+1][j]=f[i][j-t]*C(j,t)
f[i+1][j]=f[i][j−t]∗C(j,t)
怎么理解第二个
d
p
dp
dp式呢,我们先给这些相同的数预留
t
t
t个位置,求组合即可。
这样我们就得到了一个
O
(
n
4
)
O(n^{4})
O(n4)的算法。
0x03 代码
我貌似是第一个
A
A
A的,也是第一个交题解的qwq。
#include <cstdio>
#include <cstring>
#define LL long long
const LL MOD = 1e9+7;
LL read()
{
LL x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=x*10+(c^48),c=getchar();
return x*flag;
}
LL n,a[105],pre[105],inv[10005],fac[10005];
LL f[105][10005],Case;
void init(LL n)
{
inv[0]=inv[1]=fac[0]=fac[1]=1;
for(LL i=2;i<=n;i++)
inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
for(LL i=2;i<=n;i++)
inv[i]=inv[i]*inv[i-1]%MOD;
for(LL i=1;i<=n;i++)
fac[i]=fac[i-1]*i%MOD;
}
LL C(LL n,LL m)
{
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{
init(10000);
while(~scanf("%d",&n))
{
memset(f,0,sizeof f);
for(LL i=1;i<=n;i++)
{
a[i]=read();
pre[i]=pre[i-1]+a[i];
}
f[0][0]=1;
for(LL i=1;i<=n;i++)
for(LL j=0;j<=pre[i];j++)
for(LL k=0;k<=a[i] && k<=j;k++)
f[i][j]=(f[i][j]+f[i-1][j-k]*C(j,k))%MOD;
LL ans=0;
for(LL i=1;i<=pre[n];i++)
ans=(ans+f[n][i])%MOD;
printf("Case %lld: %lld\n",++Case,ans);
}
}