您的位置:首页 > 其它

[JLoi 2016] bzoj4559 成绩比较 [容斥原理]

2018-03-13 22:21 429 查看
Description:

GG系共有nn位同学,MM门必修课。这NN位同学的编号为00到N−1N−1的整数,其中B神的编号为0
4000
0号。这M门必修课编号为00到M−1M−1的整数。一位同学在必修课上可以获得的分数是11到UiUi中的一个整数。如果在每门课上AA获得的成绩均小于等于B获得的成绩,则称AA被BB碾压。在BB神的说法中,GG系共有KK位同学被他碾压(不包括他自己),而其他N−K−1N−K−1位同学则没有被他碾压。D<
20b64
span style="display: inline-block; overflow: hidden; vertical-align: -0.059em; border-left: 0px solid; width: 0px; height: 0.941em;">D神查到了B神每门必修课的排名。这里的排名是指:如果BB神某门课的排名为RR,则表示有且仅有R-1位同学这门课的分数大于BB神的分数,有且仅有N−RN−R位同学这门课的分数小于等于BB神(不包括他自己)。我们需要求出全系所有同学每门必修课得分的情况数,使其既能满足BB神的说法,也能符合DD神查到的排名。这里两种情况不同当且仅当有任意一位同学在任意一门课上获得的分数不同。你不需要像DD神那么厉害,你只需要计算出情况数模109+7109+7的余数就可以了。

Solution:

需要计算两个部分,两个部分乘起来即是答案。

第一个部分是碾压kk个人且满足排名的情况下所有人的排名与BB的排名的大小关系的方案数。第二个部分则是每个人分数满足条件的方案数,两个部分乘起来即是答案,因为每种大小关系都可以映射到对应的分数。

第一部分容斥计算,恰好kk个转化为至少kk个是常用技巧。用f[i]f[i]表示至少碾压i个人的方案数,那么f[i]=Cin−1∗∏mj=1Crnk[j]n−i−1f[i]=Cn−1i∗∏j=1mCn−i−1rnk[j],所以ans1=∑n−1i=kCki∗(−1)i−k∗f[i]ans1=∑i=kn−1Cik∗(−1)i−k∗f[i]

第二部分考虑枚举BB的分数xx,那么对于一个xx方案数是xn−rnk∗(u−x)rnk−1xn−rnk∗(u−x)rnk−1

ans2=∑mi=1∑u[i]x=1xn−rnk[i]∗(u−x)rnk[i]−1ans2=∑i=1m∑x=1u[i]xn−rnk[i]∗(u−x)rnk[i]−1

二项式定理展开(u−x)rnk[i]−1(u−x)rnk[i]−1,那么对于每门课的方案数是

∑ux=1∑rnk−1i=1ui∗Cirnk−1∗xn−i−1∗(−1)rnk−i−1∑x=1u∑i=1rnk−1ui∗Crnk−1i∗xn−i−1∗(−1)rnk−i−1

交换求和顺序

∑rnk−1i=1ui∗Cirnk−1∗(−1)rnk−i−1∗∑ux=1xn−i−1∑i=1rnk−1ui∗Crnk−1i∗(−1)rnk−i−1∗∑x=1uxn−i−1

预处理伯努利数求∑ux=1xn−i−1∑x=1uxn−i−1

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 205, P = 1e9 + 7;
int n, m, k;
ll ans1, ans2 = 1;
int u
, rnk
;
ll c

, s

, inv
, b
;
ll power(ll x, ll t) {
ll ret = 1;
for(; t; t >>= 1, x = x * x % P) {
if(t & 1) {
ret = ret * x % P;
}
}
return ret;
}
ll calc(int n, int k) {
ll fac = 1, ans = 0;
for(int i = 1; i <= k + 1; ++i)
{
fac = fac * (n + 1) % P;
ans = (ans + c[k + 1][i] * b[k + 1 - i] % P * fac % P) % P;
}
ans = ans * inv[k + 1] % P;
return ans;
}
int main() {
scanf("%d%d%d", &n, &m, &k);
c[0][0] = 1;
s[0][0] = 1;
for(int i = 1; i <= 200; ++i) {
c[i][0] = 1;
for(int j = 1; j <= i; ++j) {
c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
}
}
for(int i = 1; i <= m; ++i) {
scanf("%d", &u[i]);
}
for(int i = 1; i <= m; ++i) {
scanf("%d", &rnk[i]);
}
inv[1] = 1;
for(int i = 1; i < N; ++i) {
if(i != 1) inv[i] = (P - P / i) * inv[P % i] % P;
}
b[0] = 1;
for(int i = 1; i < N - 1; ++i) {
for(int j = 0; j < i; ++j) {
b[i] = (b[i] + c[i + 1][j] * b[j]) % P;
}
b[i] = ((b[i] * -inv[i + 1] % P) + P) % P;
}
for(int i = k; i < n; ++i) {
ll tmp = c[i][k] * (((i - k) & 1) ? -1 : 1);
tmp = (tmp % P + P) % P;
for(int j = 1; j <= m; ++j) {
tmp = tmp * c[n - i - 1][rnk[j] - 1] % P;
}
ans1 = (ans1 + tmp * c[n - 1][i] % P) % P;
}
for(int i = 1; i <= m; ++i) {
ll tmp = 0, pw = 1;
for(int j = 0; j < rnk[i]; ++j, pw = pw * u[i] % P) {
tmp = (tmp + power(u[i], j) * c[rnk[i] - 1][j] % P * calc(u[i], n - j - 1) * (((rnk[i] - j - 1) & 1) ? -1 : 1) % P) % P;
tmp = (tmp + P) % P;
}
ans2 = ans2 * tmp % P;
}
printf("%lld\n", ans1 * ans2 % P);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: