zoukankan      html  css  js  c++  java
  • 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;
    }
    

  • 相关阅读:
    【miscellaneous】 GStreamer应用开发手册学习笔记之基础概念介绍
    【miscellaneous】gstreamer构建的简单方法
    【miscellaneous】gstreamer构建的简单方法
    【miscellaneous】理解Gstreamer架构
    【miscellaneous】理解Gstreamer架构
    【miscellaneous】基于gstreamer的实时转码
    【miscellaneous】基于gstreamer的实时转码
    【miscellaneous】各种音视频编解码学习详解
    【miscellaneous】各种音视频编解码学习详解
    【miscellaneous】MPEG2、MPEG4、H264的差异
  • 原文地址:https://www.cnblogs.com/mcggvc/p/12466581.html
Copyright © 2011-2022 走看看