CF896D Nephren Runs a Cinema(组合计数+数论)

链接CF896D Nephren Runs a Cinema

大爱奈芙莲。。。。

个人感觉这题和 P3266 [JLOI2015]骗我呢有点像。
首先可以考虑dp,\(f[i][j]\)为排到第 i 个人,当前剩余 j 张50元钱的方案数,转移方程显然,超时也显然。

受P3266的启发,考虑放到二维坐标系中,x坐标表示排到了哪一位,y坐标表示剩余50元钱的数量,先不考虑vip用户,假设最后剩余 p 张50元钱,那么方案数就是从\((0, 0)\)向右上或向右下走到\((n, p)\) 的方案数,在 n 步中,向上的步数 - 向下的步数 = p,向上的步数 + 向下的步数 = n,可解得方案数为\(C_{n}^{(n - p) / 2}\), 其中有不合法的方案数,即触碰了直线\(y = -1\)的路径,我们需要减去从\((0, -2)\)\((n, p)\) 的方案数,即\(C_{n}^{(n - p) / 2 - 1}\)。最后因为从 l 到 r 是连续的,所以我们将所有式子算出来相加可以消掉中间项,最后只剩\(C_{n}^{(n - l) / 2} - C_{n} ^ {(n - r - 1) / 2}\),我们需要枚举vip用户数量,最算的时候把总数减去vip数量,求出的结果乘上\(C_{n}^{i}\) (i为vip数量)。

因为模数不是质数,无法用肥妈小定理,用exgcd也有可能找不到逆元,所以我们将模数 p 质因数分解,在预处理阶乘的时候把包含的 p 的质因子取出来,记下指数,然后再乘回来。算逆元直接用欧拉定理。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long lld;
const int N = 100005;
lld p, n, l, r;
lld pr[N], inv[N], f[N], c[N][40], phi;
//pr[]为p的质因子,c[i][j]为i的阶乘中p的第j个质因子的指数
int cnt = 0;
lld powe(lld a, lld b) {
	lld base = 1;
	while(b) {
		if(b & 1) base = (base * a) % p;
		a = (a * a) % p;
		b >>= 1;
	}
	return base;
}
void calc() {
	f[0] = inv[0] = 1;
	f[1] = inv[1] = 1;
	lld x = p;
	for(lld i = 2; i * i <= x; i++) {//分解p
		if(x % i) continue;
		pr[++cnt] = i;
		while(!(x % i)) x /= i;
	}
	if(x > 1) pr[++cnt] = x;
	for(int i = 2; i <= n; i++) {
		lld x = i;
		for(int j = 1; j <= cnt; j++) {
			c[i][j] = c[i - 1][j];//指数加上之前的
			while(!(x % pr[j])) x /= pr[j], c[i][j]++;
		}
		f[i] = f[i - 1] * x % p;//阶乘
		inv[i] = powe(f[i], phi - 1);//逆元
	}
}
lld comb(lld _n, lld m) {
	if(m < 0 || _n < m || n <= 0) return 0;
	if(m == 0) return 1;
	lld tmp = f[_n] * inv[m] % p * inv[_n - m] % p;
	for(int i = 1; i <= cnt; i++) {
		tmp = (tmp * powe(pr[i], c[_n][i] - c[m][i] - c[_n - m][i])) % p;//乘回来
	}
	return tmp;
}
int main() {
//	freopen("data.in", "r", stdin);
	scanf("%lld%lld%lld%lld", &n, &p, &l, &r);
	phi = p; lld d = p;//算phi函数
	for(lld i = 2; i * i <= d; i++) {
		if(d % i) continue;
		phi = phi / i * (i - 1);
		while(!(d % i)) d /= i;
	}
	if(d > 1) phi = phi / d * (d - 1);
	calc(); lld ans = 0; 
	r = min(n, r);
	for(lld i = 0; i <= n - l; i++) {
		lld tmp = (comb(n - i, (n - i - l) >> 1) - comb(n - i, (n - i - r - 1) >> 1) + p) % p;
		ans += (tmp * comb(n, i)) % p; 
		ans = ans % p;
	}
	printf("%lld", ans);
	return 0;
}

posted @ 2020-03-12 00:00  Mcggvc  阅读(154)  评论(0编辑  收藏  举报