[国家集训队]礼物 x exLucas板子

题目链接

一眼答案( tot 是 wi 的和
( n t o t ) ∗ ( t o t w m ) ∗ … ∗ ( w 1 w 1 ) \tbinom{n}{tot} * \tbinom{tot}{w_m} * … * \tbinom{w_1}{w_1} (totn)(wmtot)(w1w1)
其实这里就可以直接exLucas板子了
但其实可以更简单
然后拆一下式子
! ( n ) / ! ( n − t o t ) / ! ( t o t ) ∗ ! ( t o t ) / ! ( t o t − w m ) / ! ( w m ) … … !(n) / !(n - tot) / !(tot) * !(tot) / !(tot - w_m) / !(w_m) …… !(n)/!(ntot)/!(tot)!(tot)/!(totwm)/!(wm)
然后发现可以消掉
! ( n ) / ! ( n − t o t ) / ! ( w m ) … … / ! ( w 1 ) !(n) / !(n - tot) / !(w_m) …… / !(w_1) !(n)/!(ntot)/!(wm)/!(w1)
然后现在就是要求这个别致的小东西了
注意模数不是质数哦
需要exgcd求逆元 CRT(中国剩余定理)合并
懒得写了
实现可以参考一下这篇博客

附上代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e5 + 2; 
long long P, ans; 
long long n, m, w[10], tot;

void exgcd(long long a, long long b, long long& x, long long& y){
	if(!b){x = 1, y = 0; return ;}
	exgcd(b, a % b, y, x); y -= (a / b) * x;
}//ok

long long inv(long long a, long long b){
	long long x, y;
	exgcd(a, b, x, y);
	x = (x % b + b) % b;
	return x;
}//ok

long long qpow(long long x, long long y, long long mod){
	long long res = 1;
	while(y){
		if(y & 1) res = res * x % mod;
		y >>= 1; x = x * x % mod;
	}
	return res;
}//ok

long long fac(long long x, long long p1, long long p2){
	if(!x) return 1;
	long long res = 1;
	for(int i = 2; i <= p2; ++i)
	    if(i % p1) res = res * i % p2;
	res = qpow(res, x / p2, p2);
	for(int i = 2; i <= x % p2; ++i)
	    if(i % p1) res = res * i % p2;
	return res * fac(x / p1, p1, p2) % p2;
}//ok

struct P{
	long long w;
	long long s1[N], s2[N];
	int top;
	long long CRT(long long x, long long p2){
		return x * (w / p2) % w * inv(w / p2, p2) % w;
	}
    void init(){
    	int x = w;
    	for(int i = 2; i * i <= w; ++i)
		if(!(x % i)){
    		s1[++top] = i;
    		s2[top] = 1;
    		while(!(x % i)){x /= i; s2[top] *= i;}
		}
		if(x > 1){
			s1[++top] = x; s2[top] = x;
		}
    }
}p;//ok

long long solve(long long p1, long long p2){
	long long res = 1;
	res = fac(n, p1, p2) * inv(fac(n - tot, p1, p2), p2) % p2;
	for(int i = 1; i <= m; ++i)
	    res = res * inv(fac(w[i], p1, p2), p2) % p2;
	long long cnt = 0;
	for(long long i = n; i; i /= p1) cnt += i / p1;
	for(long long i = n - tot; i; i /= p1) cnt -= i / p1;
	for(int j = 1; j <= m; ++j){
	    for(long long i = w[j]; i; i /= p1) cnt -= i / p1;
	}
	return p.CRT(res * qpow(p1, cnt, p2), p2);
}

int main() {
    scanf("%lld%lld%lld", &p.w, &n, &m);
    p.init();
    
    for(int i = 1; i <= m; ++i){
        scanf("%lld", &w[i]);
    	tot += w[i];
    }
    if(tot > n){
    	printf("Impossible\n");
    	return 0;
    }
    
    for(int i = 1; i <= p.top; ++i){
	//printf("%d %lld %lld\n", p.top, p.s1[i], p.s2[i]);
    	ans = (ans + solve(p.s1[i], p.s2[i])) % p.w;//这里要记得取模啊qvq
    }
    
    printf("%lld", ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值