P3270 [JLOI2016]成绩比较(拉格朗日插值,组合数学,容斥原理)

洛谷题目链接
在这里插入图片描述


计数类的问题,有点难想

先考虑满足每一科排名的关系:枚举每门课, a n s = i = 1 m C ( n 1 , r [ i ] ) ans = \displaystyle\prod_{i = 1}^mC(n - 1,r[i])

由于 B神只碾压了 k 个人,有 n k 1 n - k - 1 个人没有碾压,令 t = n k 1 t = n - k - 1 ,考虑先选出这 t t 个人,每一科比 B神 高的人 只在这 t t 个人里选,但要满足这 t t 个人 至少有一门 比 B神高,先考虑忽略这个条件: a n s = C ( n 1 , t ) i = 1 m C ( t , r [ i ] ) ans = \displaystyle C(n - 1,t)\prod_{i = 1}^mC(t,r[i])

f ( t ) = i = 1 m C ( t , r [ i ] ) f(t) = \displaystyle\prod_{i = 1}^mC(t,r[i]) ,这部分不一定 t 个人 都有一门比 B 神高。
这个组合数学模型可以转化为:有 t t 个盒子,每次放 r [ i ] r[i] 个小球,共放 m m 次,盒子不能为空,有多少种方案。
考虑用容斥得到答案:枚举有几个空盒子进行容斥,也可以用二项式反演:
f ( t ) f(t) 表示在 t 个盒子中一共放 m 次,每次放 r [ i ] r[i] 个小球,盒子可以为空的方案总数, g ( t ) g(t) f ( t ) f(t) 加上一条限制:每个盒子都不能为空的方案总数。

显然 f ( t ) = i = 0 t C ( t , i ) g ( i ) f(t) = \displaystyle\sum_{i = 0}^t C(t,i)*g(i) ,二项式反演得到 : g ( t ) = i = 0 t ( 1 ) t i C ( t , i ) f ( i ) g(t) = \displaystyle\sum_{i = 0}^t(-1)^{t - i}*C(t,i)*f(i)

也可以枚举空盒进行容斥: f ( t ) = i = 0 t C ( t , i ) g ( t i ) f(t)= \displaystyle\sum_{i = 0}^t C(t,i)*g(t-i) g ( t ) = i = 0 t ( 1 ) i C ( t , i ) f ( t i ) g(t)=\displaystyle\sum_{i = 0}^{t}(-1)^i*C(t,i)*f(t - i)
反演时等号两边的参数形式不变, 1 -1 的幂次和等号右边的参数之和等于 t t

这样可以得到仅考虑每一门排名的方案数: a n s = x = 0 t ( 1 ) t x C ( t , x ) i = 1 m C ( x , r [ i ] ) ans = \displaystyle\sum_{x = 0}^t(-1)^{t - x}*C(t,x)*\prod_{i = 1}^mC(x,r[i])

接下来考虑不同分数的方案数,考虑枚举分数, r e s = j = 1 U i j n R i 1 ( U i j ) R i res = \displaystyle\sum_{j = 1}^{U_i}j^{n - R_i - 1}*(U_i - j)^{R_i} ,由于 U i U_i 非常大,观察这个式子,是一个 j j 为自变量的 n R i 1 n - R_i - 1 次多项式和 R i R_i 次多项式的乘积之和,显然这两个多项式的乘积得到的是 j j 为自变量的 n 1 n - 1 次多项式,它的和又得到 以 U i U_i 为自变量的 n n 多项式,使用拉格朗日插值插入 n + 1 n + 1 个点

最后的答案就是: C ( n 1 , t ) x = 0 t ( 1 ) t x C ( t , x ) i = 1 m C ( x , r [ i ] ) j = 1 U i j n R i 1 ( U i j ) R i \displaystyle C(n - 1,t)*\sum_{x = 0}^t(-1)^{t - x}*C(t,x)*\prod_{i = 1}^mC(x,r[i])*\sum_{j = 1}^{U_i}j^{n - R_i - 1}*(U_i - j)^{R_i}

后面部分可以 n 3 log n n^3\log n 预处理插值(预处理幂次可以降低到 n 3 n^3 ),总体复杂度为 n 3 log n n^3\log n


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int mx;
inline ll add(ll x, ll y) {
  	x += y;
  	if (x >= mod) x -= mod;
  	return x;
}

inline ll sub(ll x, ll y) {
	x -= y;
	if (x < 0) x += mod;
	return x;
}

inline ll mul(ll x, ll y) {
  	return x * y % mod;
}
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if (b & 1) r = mul(r,a);
		b >>= 1;
		a = mul(a,a);
	}
	return r;
}
ll g[maxn],f[maxn],u[maxn],r[maxn],k,n,m;
ll fac[maxn],ifac[maxn];
ll cal(ll g[maxn],ll x) {			//拉格朗日插值计算多项式
	if (x <= mx) return g[x];
	ll tmp = 1,inv,ans = 0;
	for (int i = 1; i <= mx; i++)
		tmp = mul(tmp,x - i);
	for (int i = 1; i <= mx; i++) {
		ll res = 1, inv = fpow(x - i,mod - 2);
		res = mul(res,g[i]);
		res = mul(res,ifac[i - 1]);
		res = mul(res,ifac[mx - i]);
		res = mul(res,inv);
		res = mul(res,tmp);
		if ((mx - i) & 1) res = mul(res,-1);
		if (res < 0) res += mod;
		ans = add(ans,res);
	}
	return ans;
}
ll C(int x,int y) {
	if (y > x || x < 0) return 0;
	return mul(mul(ifac[y],ifac[x - y]),fac[x]);
}
int main() {
	fac[0] = 1;
	for (int i = 1; i <= 1000; i++)
		fac[i] = mul(fac[i - 1],i);
	ifac[1000] = fpow(fac[1000],mod - 2);
	for (int i = 1000 - 1; i >= 0; i--)
		ifac[i] = mul(ifac[i + 1],i + 1);
	scanf("%d%d%d",&n,&m,&k);
	for (int i = 1; i <= m; i++) {
		scanf("%d",&u[i]);
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d",&r[i]);
	}
	ll ans = 0;
	mx = n + 2;
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= mx; j++) {
			g[j] = 0;
			for (int k = 1; k <= j; k++) {
				ll t1 = fpow(k,n - r[i]), t2 = fpow(sub(j,k),r[i] - 1);
				g[j] = add(g[j],mul(t1,t2));
			}
		}
		f[i] = cal(g,u[i]);
	}
	for (int t = 0; t <= n - k - 1; t++) {
		ll res = C(n - k - 1,t);
		if ((n - k - 1 - t) & 1) res = mod - res;
		for (int i = 1; i <= m; i++) {
			res = mul(res,C(t,r[i] - 1));
			res = mul(res,f[i]);
		}
		ans = add(ans,res);
	}
	ans = mul(ans,C(n - 1,n - k - 1));
	printf("%lld\n",ans);
	return 0;
}
发布了332 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41997978/article/details/104295066
今日推荐