BZOJ 4559 成绩比较

题意:https://www.lydsy.com/JudgeOnline/problem.php?id=4559

Sol:

第一眼看起来就是个稍微麻烦的组合数

但是发现如果钦点哪些同学分数在某科目上分数比B神低以后的方案,就会出现没有被钦

点碾压的同学也会被碾压,(后面钦点分数时可能钦点的一直是同一批人导致人数不够不被碾压的人数)

于是可以考虑容斥,用至少 i i i个人的方案算出恰好 k k k个人的方案。

可以得到

因为被碾压的不能超过B君最大的一个排名,所以定义 u p = m i n { n − R i } up=min\{n-R_i\} up=min{nRi}

∑ i = k u p ( − 1 ) i − k ( i k ) ( n − 1 i ) ∏ j = 1 m ( n − i − 1 R j − 1 ) ∑ x = 1 U j ( U j − x ) R j − 1 x n − R j \sum_{i=k}^{up}(-1)^{i-k}\binom{i}{k}\binom{n-1}{i}\prod_{j=1}^m\binom{n-i-1}{R_j-1}\sum_{x=1}^{U_j}(U_j-x)^{R_j-1}x^{n-R_j} i=kup(1)ik(ki)(in1)j=1m(Rj1ni1)x=1Uj(Ujx)Rj1xnRj

发现后边枚举分数的 ∑ \sum 实际上是在对关于 U j U_j Uj的多项式做前缀和,因此是个 n n n次多项式,拉格朗日插值即可,因为x是自己取,所以可以做线性插值,复杂度 n 2 n^2 n2

#include
#include
#include
const int N = 1e2+9;
typedef long long LL;
const int upmax = 1e2+5;
#define p 1000000007
#define debug printf("GG\n")
inline int min(int a, int b) {return a > b ? b : a;}
inline LL FST(LL b, LL k) {
  if (!b && k > 0) return 0;
  LL ans = 1LL;
  while (k) {
    if (k & 1) ans = ans * b % p; b = b * b % p, k >>= 1;
  } return ans;
}
int n, m, k, rk[N]; LL U[N];
LL lagr(LL *ax, LL *ay, int up, LL x) {
  LL res = 0;
  for (int i = 1; i <= up; i++) {
    LL s1 = 1, s2 = 1;
    for (int j = 1; j <= up; j++) {
      if (i == j) continue;
      s1 = (LL)(s1 * ((x - ax[j]) % p + p) % p) % p;
      s2 = (LL)(s2 * ((ax[i] - ax[j]) % p + p)) % p;
    } res = (res + (LL)(s1 * FST(s2, p - 2)) % p * ay[i] % p) % p;
  } return res;
} LL tx[N], ty[N], poly[N]; LL inv[N], fac[N];
inline LL C(int n, int m) {
  return fac[n] * inv[n - m] % p * inv[m] % p;
}int up;
void init() {
  scanf("%d%d%d", &n, &m, &k);
  inv[1] = 1LL, fac[1] = 1LL, fac[0] = inv[0] = 1LL;
  for (int i = 2; i <= upmax; i++) inv[i] = inv[i - 1] * FST(i, p - 2) % p;
  for (int i = 2; i <= upmax; i++) fac[i] = fac[i - 1] * (LL)i % p;
  for (int i = 1; i <= m; i++) scanf("%lld", &U[i]);
  for (int i = 1; i <= m; i++) scanf("%d", &rk[i]);
  for (int i = 1; i <= m; i++) {
    int R = rk[i]; 
    for (int j = 1; j <= n + 1; j++) {
      tx[j] = j, ty[j] = 0;
      for (int x = 1; x <= j; x++) 
        ty[j] = (ty[j] + FST(j - x, R - 1) * FST(x, n - R) % p) % p;
    } poly[i] = lagr(tx, ty, n + 1, U[i]);
  }
  up = n;
  for (int i = 1; i <= m; i++) up = min(up, n - rk[i]);
} 
void solve() {
  LL ans = 0; 
  for (int i = k; i <= up; i++) {
    LL res = ((i - k) & 1 ? -1 : 1) * C(i, k) % p * C(n - 1, i) % p;
    for (int j = 1; j <= m; j++) 
      res = res * poly[j] % p * C(n - i - 1, rk[j] - 1) % p;
    ans = (ans + res) % p;
  } printf ("%lld\n", (ans % p + p) % p);
} 
void debugR() {
  LL ans = 0;
  for (int i = k; i <= up; i++) {
    LL res = ((i - k) & 1 ? -1 : 1) * C(n - 1, i) % p * C(i, k) % p;
    res = (res % p + p) % p;
    for (int j = 1; j <= m; j++) {
      LL res2 = 0;
      for (int og = 1; og <= U[j]; og++) {
        res2 = (res2 + FST(U[j] - og, rk[j] - 1) * FST(og, n - rk[j]) % p) % p;
      }
      res = res2 * res % p * C(n - 1 - i, n - rk[j] - i) % p;
    } ans = (ans + res) % p;
  } printf("%lld\n",(ans % p + p) % p);
}
int main() {
  init(), solve();
 // debugR();
}

你可能感兴趣的:(数数,拉格朗日插值,容斥原理,二项式反演,题目)