[BZOJ 2142]禮物

OTZ Po姐姐
題目的意思是求C(n, m1) * C(n-m1, m2) * C(n-m1-m2, m2)….(mod P)
感覺是小學數學。。
化簡一下就是 n! / πmi=1 w[i]!
但是mod的數不一定是質數,不能用費馬小定理。
paii105
我們可以對於每個質數塊 paii 分別計算答案
然後用中國剩餘定理合併
考慮一個較小的質數p 以及它在P 中的質數塊pa ,對於每一個階乘我們可以把階乘化成 aipbi 的形式
此時ai 對於p 是有逆元的
然後用指數相加或相減,ai相乘,除法就求逆元
現在的問題是如何處理n過大的問題
我們考慮一個數n!
舉個栗子
p=3,a=2,pa=9
19!=1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19
然後mod 9
然後把和3有關的東西提出來
變成
19! = 1*2*4*5*7*8*10*11*13*14*16*17*19 * 36 * (1*2*3*4*5*6)
有一些是mod 9同餘的
1*2*4*5*7*8=10*11*13*14*16*17(mod 9)
所以可以按照pa 分塊然後快速冪
至於6!遞歸處理

乘法的時候忘記mod掛long long好幾次=-=。。

#include 
#define maxn 100010
using namespace std;

typedef long long ll;

ll P, sum;

int n, m, w[10];

int p[maxn], num[maxn], p_a[maxn], cnt;

void FJ(ll now){
	for(int i = 2; i * i <= now; i ++){
		if(now % i == 0){
			cnt ++;
			num[cnt] = 0;
			p_a[cnt] = 1;
			p[cnt] = i;
			while(now % i == 0){
				p_a[cnt] *= i;
				now /= i;
				num[cnt] ++;
			}
		}
	}

	if(now != 1){
        cnt ++;
		num[cnt] = 1;
		p_a[cnt] = p[cnt] = now;
	}
	//for(int i = 1; i <= cnt; i ++)cout << p[i] << ' ' << p_a[i] << ' ' << num[i] << endl;
}

ll power_mod(ll a, ll b, ll mod){
	ll ans = 1;
	a %= mod;
	while(b){
		if(b & 1)ans = ans * a % mod;
		b >>= 1;
		a = a * a % mod;
	}return ans % mod;
}

//ax + by = 1;
void Exgcd(ll a, ll b, ll& d, ll& x, ll& y){
	if(!b){d = a; x = 1; y = 0; return;}
	Exgcd(b, a%b, d, y, x);
	y -= x * (a / b);
}

ll d, x, y;
ll inv(ll a, ll b){
    Exgcd(a, b, d, x, y);
    return (x + b) % b;
}

ll ans[maxn], M, now;

int pos;

typedef pair Point;

Point operator * (const Point& a, const Point& b){
	return make_pair(a.first * b.first % now, a.second + b.second);
}

Point operator / (const Point& a, const Point& b){
	return make_pair(a.first * inv(b.first, now) % now, a.second - b.second);
}

Point Fac(ll x){
	Point ret = make_pair(1ll, x / p[pos]);
	for(ll i = 1; i < p_a[pos]; i ++)
	    if(i % p[pos])ret.first = ret.first * i % p_a[pos];
	ret.first = power_mod(ret.first, x / p_a[pos], p_a[pos]);
	for(ll i = x - x % p_a[pos] + 1; i <= x; i ++)
	    if(i % p[pos])ret.first = ret.first * i % p_a[pos];
	if(ret.second)ret = ret * Fac(ret.second);
	return ret;
}

void work(int Id){
    now = p_a[Id], pos = Id;
	Point o = Fac(n);
	for(int i = 1; i <= m; i ++)
	    o = o / Fac(w[i]);
	ans[Id] = o.first * power_mod(p[Id], o.second, now) % p_a[Id];
}

ll CRT(){
	ll ret = 0, w;
	for(int i = 1; i <= cnt; i ++){
		now = p_a[i], w = M / now;
		ret = (ret + inv(w, now) * w % M * ans[i] % M) % M;
	}
	ret %= M;
	if(ret < 0)ret += M;
	return ret;
}

int main(){
	cin >> P >> n >> m;
	M = P;
	sum = 0;
	for(int i = 1; i <= m; i ++)
	    cin >> w[i], sum += w[i];
	if(sum > n){puts("Impossible");return 0;}
	FJ(P);w[++ m] = n - sum;
	for(int i = 1; i <= cnt; i ++)work(i);
	cout << CRT() << endl;
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章