您的位置:首页 > 其它

BZOJ 4559 [JLoi2016]成绩比较

2017-02-22 15:09 197 查看
DP+组合计数

不难想到记f[i][j]表示统计到第i门课,此时j个人被碾压的方案数。

f[i][j] = f[i-1][k] * C[k][j] * C[n-k-1][n-rank[i]-j] * P[i] (k >= j)

意义就是从前面的k个人里面选出j个人继续碾压,再选出一些人来排在自己的排名后面,再乘上分配给n个人1~U[i]的分数的合法方案数P[i]。

我们发现 P[i]=∑U[i]j=1jn−rank[i]∗(U[i]−j)rank[i]−1

二项式展开,再把外面的j乘进去,就变成一个求类似∑ai=1ib的问题,这东西显然可以强行插值。然而有一个更 短 妙的做法,不想插值的同学请点这里

#include<cstdio>
#include<cstring>
#define N 105
#define MOD 1000000007
using namespace std;
namespace runzhe2000
{
typedef long long ll;
int n, m, d, u
, r
, g
, C

, f

;
int fpow(int a, int b)
{
int r = 1;
for(; b; b>>=1)
{
if(b&1) r = (ll)r*a%MOD;
a = (ll)a*a%MOD;
}
return r;
}
void main()
{
scanf("%d%d%d",&n,&m,&d);
for(int i = 1; i <= m; i++) scanf("%d",&u[i]);
for(int i = 1; i <= m; i++) scanf("%d",&r[i]);
C[0][0] = 1; for(int i = 1; i <= n+1; i++) {C[i][0] = 1; for(int j = 1; j <= i; j++) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % MOD; }
f[0][n-1] = 1;
for(int i = 1; i <= m; i++)
{
g[0] = u[i];
for(int j = 1; j <= n; j++)
{
g[j] = (fpow(u[i]+1, j+1) - 1 + MOD) % MOD;
for(int k = 0; k < j; k++)
(g[j] -= (ll)C[j+1][k] * g[k] % MOD ) %= MOD;
g[j] = (ll)g[j] * fpow(j+1, MOD-2) % MOD;
}
int P = 0, inv = fpow(u[i], MOD-2), v = fpow(u[i], r[i]-1), q = 1;
for(int j = 0; j <= r[i]-1; j++)
{
(P += (ll)q * C[r[i]-1][j] % MOD * v % MOD * g[n-r[i]+j] % MOD) %= MOD;
v = (ll) v * inv % MOD;
q = -q;
}
for(int j = d; j <= n; j++)
for(int k = j; k <= n; k++)
if(n-r[i]-j >= 0)(f[i][j] += (ll)f[i-1][k] * C[k][j] % MOD * C[n-k-1][n-r[i]-j] %MOD * P % MOD) %= MOD;
}
printf("%d\n",(f[m][d]+MOD)%MOD);
}
}
int main()
{
runzhe2000::main();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: