ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【ybt金牌导航8-3-6】【luogu P3270】成绩比较(DP)(拉格朗日插值)

2021-12-09 12:32:12  阅读:238  来源: 互联网

标签:分数 return P3270 luogu ll ybt 109 binom mo


成绩比较

题目链接:ybt金牌导航8-3-6 / luogu P3270

题目大意

有 n 个人,第一个是自己,然后每个人每科有分数,给你科目数量,科目最高分,你的排名(排名比你高的分数都比你大,排名比你低的分数都比你小)。
然后告诉了你你完胜了多少个人(即每一科都大于等于它),然后问你有多少种满足条件的分数情况。
只要有一个人有一科的分数不同,那这个分数情况就是不同的。

思路

我们考虑 DP,设 \(f_{i,j}\) 为当前看了前 \(i\) 科,暂时完胜 \(j\) 个人。
显然初始化 \(f_{0,n-1}=1\)。

然后考虑转移:(先把式子给出来)
\(f_{i,j}=\sum\limits_{l=j}^kf_{i-1,l}\binom{j}{l}\binom{n-r_i-j}{n-1-l}\sum\limits_{p=1}^{u_i}p^{n-r_i}(u_i-p)^{r_i-1}\)

我们可以把这个转移分成三部分分别解释:
\(f_{i-1,l}\):这个显然。

\(\binom{j}{l}\binom{n-r_i-j}{n-1-l}\):我们考虑从原本 \(l\) 个完胜的选出 \(j\) 个继续完胜,那就是 \(\binom{j}{l}\),然后你胜的次数就用了 \(j\),一共能胜的次数是 \(n-r_i\)(败的次数是 \(r_i-1\)),还有 \(n-r_i-j\) 次,随机分配给剩下的。
然后要注意的是部分分派给 \(l\) 个中没有被选到的,所以是 \(\binom{n-r_i-j}{n-1-l}\)。

\(\sum\limits_{p=1}^{u_i}p^{n-r_i}(u_i-p)^{r_i-1}\):这个就是你要给所有人分配分数(你前面只是分配了排名)
然后你就考虑枚举自己的分数,那我们已经知道它胜了 \(n-r_i\) 个人,败了 \(r_i-1\) 个人。
那其他人之间的胜负我们不用管,所以被胜的人可以选 \(1\sim p\)(记得可以相同),一共是 \(p\) 种,所以是 \(p^{n-r_i}\)。被败的人可以选 \(p+1\sim u_i\),一共是 \(u_i-p\),所以是 \((u_i-p)^{r_i-1}\)。

然后我们发现 \(u_i\) 很大。
然后我们考虑优化这一维,观察发现 \(n-r_i\) 和 \(r_i-1\) 在枚举 \(i\) 的时候已经固定。
那就相当于 \(\sum\limits_{i=1}^yx^k(y-x)^l\) 这种形式。

然后你会发现这两个分别是 \(k,l\) 项的多项式,然后乘在一起就是 \(k+l\) 项,在原式中 \(k=n-r_i,l=r_i-1\),所以 \(k+l=n-1\),那前缀和一下就是 \(n\) 项的多项式!
那多项式我们就可以直接用拉格朗日插值来求 \(u_i\) 时的值了。
(我写题的时候没细看用了 \(n+1\) 项的,不过都无所谓可以过)

然后就搞好了。

代码

#include<cstdio>
#define ll long long
#define mo 1000000007

using namespace std;

ll n, m, k, u[109], r[109], inv_mo1;
ll jc[109], inv[109], f[109][109];
ll y[109], pre[109], suf[109], lft;

ll ksm(ll x, ll y) {
	x = (x % mo + mo) % mo;
	ll re = 1;
	while (y) {
		if (y & 1) re = re * x % mo;
		x = x * x % mo;
		y >>= 1;
	}
	return re;
}

ll C(ll n, ll m) {
	if (n < m) return 0;
	if (m < 0) return 0;
	return jc[n] * inv[m] % mo * inv[n - m] % mo;
}

ll query(ll k, ll n) {//拉格朗日插值
	ll re = 0;
	for (int i = 1; i <= k; i++) {
		re = (re + y[i] * pre[i - 1] % mo * suf[i + 1] % mo * inv[i - 1] % mo * inv[k - i] % mo * (((k - i) & 1) ? inv_mo1 : 1) % mo) % mo;
	}
	return re;
}

int main() {
	inv_mo1 = ksm(mo - 1, mo - 2);//预处理拉格朗日插值的逆元,这样就不用带log(组合数中也用到)
	jc[0] = 1; for (int i = 1; i <= 108; i++) jc[i] = jc[i - 1] * i % mo;
	inv[0] = inv[1] = 1; for (int i = 2; i <= 108; i++) inv[i] = inv[mo % i] * (mo - mo / i) % mo;
	for (int i = 1; i <= 108; i++) inv[i] = inv[i - 1] * inv[i] % mo;
	
	scanf("%lld %lld %lld", &n, &m, &k);
	for (int i = 1; i <= m; i++) scanf("%lld", &u[i]);
	for (int i = 1; i <= m; i++) scanf("%lld", &r[i]);
	
	f[0][n - 1] = 1;//当前 i 科,j 个碾压
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= n + 2; j++)//提前把拉格朗日插值要的东西能先求就先求,减小复杂度
			y[j] = (y[j - 1] + ksm(j, n - r[i]) * ksm(u[i] - j, r[i] - 1) % mo) % mo;
		pre[0] = 1; for (int j = 1; j <= n + 2; j++) pre[j] = pre[j - 1] * (u[i] - j + mo) % mo;
		suf[n + 2 + 1] = 1; for (int j = n + 2; j >= 1; j--) suf[j] = suf[j + 1] * (u[i] - j + mo) % mo;
		
		for (int j = k; j < n; j++) {
			for (int l = j; l < n; l++) {//DP
				lft = C(l, j) * C(n - 1 - l, n - r[i] - j) % mo;
				f[i][j] = (f[i][j] + f[i - 1][l] * lft % mo * query(n + 2, u[i]) % mo) % mo;
			}
		}
	}
	
	printf("%lld", f[m][k]);
	
	return 0;
} 

标签:分数,return,P3270,luogu,ll,ybt,109,binom,mo
来源: https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_8-3-6.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有