BZOJ 4559 [JLoi2016]成绩比较 (DP+拉格朗日插值)

题意

BZOJ4559

题解

看这,写得真好。

CODE

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 105;
const int mod = 1e9 + 7;

int n, M, K, U[MAXN], R[MAXN], c[MAXN][MAXN], f[MAXN][MAXN];

inline int qpow(int a, int b) {
	int re = 1;
	while(b) {
		if(b&1) re = 1ll * re * a % mod;
		a = 1ll * a * a % mod; b >>= 1;
	}
	return re;
}

inline int calc(int m, int rk) {
	if(m <= n+1) { //特判,不然下面的运算会有(x-xi)=0
		int re = 0;
		for(int i = 1; i <= m; ++i)
			re = (re + 1ll * qpow(i, n-rk) * qpow(m-i, rk-1)) % mod;
		return re;
	}
	int up = 1, re = 0, Div = 1, S = 0;
	for(int i = 1; i <= n+1; ++i) up = 1ll * up * (m-i) % mod;
	for(int i = 2; i <= n+1; ++i) Div = 1ll * Div * (1-i) % mod;
	for(int i = 1, Up; i <= n+1; ++i) {
		Up = 1ll * up * qpow(m-i, mod-2) % mod; //分子
		S = (S + 1ll * qpow(i, n-rk) * qpow(m-i, rk-1) % mod) % mod; //点值
		re = (re + 1ll * S * Up % mod * qpow(Div, mod-2) % mod) % mod;
		Div = 1ll * Div * qpow(i-(n+1), mod-2) % mod * i % mod; //分母
	}
	return re;
}
inline void pre() {
	c[0][0] = 1;
	for(int i = 1; i <= n; ++i) {
		c[i][0] = c[i][i] = 1;
		for(int j = 1; j < i; ++j)
			c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
	}
}
int main () {
	scanf("%d%d%d", &n, &M, &K); pre();
	for(int i = 1; i <= M; ++i) scanf("%d", &U[i]);
	for(int i = 1; i <= M; ++i) scanf("%d", &R[i]);
	f[0][n-1] = 1;
	for(int i = 1; i <= M; ++i) {
		for(int j = 0; j < n; ++j)
			for(int k = j; k <= n && k-j < R[i]; ++k)
				f[i][j] = (f[i][j] + 1ll * c[k][j] * c[n-k-1][(R[i]-1)-(k-j)] % mod * f[i-1][k] % mod) % mod;
	}
	int ans = f[M][K];
	for(int i = 1; i <= M; ++i)
		ans = 1ll * ans * calc(U[i], R[i]) % mod;
	printf("%d
", (ans + mod) % mod);
}
//d[i] = sigma_{t=1}^{U[i]} t^(n-R[i]) * (U[i]-t)^(R[i]-1)
原文地址:https://www.cnblogs.com/Orz-IE/p/12039220.html