1. 程式人生 > >[BZOJ2142]禮物-擴充套件lucas定理-中國剩餘定理

[BZOJ2142]禮物-擴充套件lucas定理-中國剩餘定理

禮物

Description

一年一度的聖誕節快要來到了。每年的聖誕節小E都會收到許多禮物,當然他也會送出許多禮物。不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的禮物會越多。小E從商店中購買了n件禮物,打算送給m個人,其中送給第i個人禮物數量為wi。請你幫忙計算出送禮物的方案數(兩個方案被認為是不同的,當且僅當存在某個人在這兩種方案中收到的禮物不同)。由於方案數可能會很大,你只需要輸出模P後的結果。

Input

輸入的第一行包含一個正整數P,表示模;
第二行包含兩個整整數n和m,分別表示小E從商店購買的禮物數和接受禮物的人數;
以下m行每行僅包含一個正整數wi,表示小E要送給第i個人的禮物數量。

Output

若不存在可行方案,則輸出“Impossible”,否則輸出一個整數,表示模P後的方案數。

Sample Input

100
4 2
1
2

Sample Output

12

HINT

【樣例說明】

下面是對樣例1的說明。

以“/”分割,“/”前後分別表示送給第一個人和第二個人的禮物編號。12種方案詳情如下:

1/23 1/24 1/34

2/13 2/14 2/34

3/12 3/14 3/24

4/12 4/13 4/23

【資料規模和約定】

設P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi為質數。

對於100%的資料,1≤n≤109,1≤m≤5,1≤pi^ci≤10^5。

題不是很難,然而被Impossible坑了整整兩個小時

(╯‵□′)╯︵┻━┻

思路:
首先可以得出求答案的式子:
ans=(nw[1])(nw[1]w[2])(nw[1]w[2]w[3])...

拆式子化簡?
確實可以拆,然而完全不需要。

考慮到模數不為質數,將模數拆成若干個質數的次冪分開計算,最後使用中國剩餘定理合併答案。
考慮到模數依舊不是質數而是質數的某個次冪,採用擴充套件lucas定理計算組合數。
那麼就做完了。無比的暴力。

唯一需要注意的一點是,記得判Impossible……

#include<bits/stdc++.h>

using
namespace std; typedef long long ll; typedef pair<ll,ll> pr; const ll Inf=1e18; ll p,n,m; ll w[19]; pr ans[10009],ori[10009]; inline ll read() { ll x=0;char ch=getchar(); while(ch<'0' || '9'<ch)ch=getchar(); while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar(); return x; } inline ll qpow(ll a,ll b,ll p=Inf) { ll ret=1; while(b) { if(b&1) ret=ret*a%p; a=a*a%p; b>>=1; } return ret; } inline ll phi(ll p,ll t) { return (p-1)*qpow(p,t-1); } inline ll inv(ll a,ll p,ll t,ll k) { ll phis=phi(p,t); return qpow(a,phis-1,k); } inline ll fac(ll x,ll p,ll k) { if(!x)return 1; ll ret=1; for(ll i=1;i<=k;i++) if(i%p) ret=ret*i%k; ret=qpow(ret,x/k,k); for(ll i=x/k*k+1ll;i<=x;i++) if(i%p) ret=ret*i%k; return ret*fac(x/p,p,k)%k; } inline ll c(ll n,ll m,ll p,ll t) { ll cnt=0,k=qpow(p,t); for(ll i=n;i;i=i/p)cnt+=i/p; for(ll i=m;i;i=i/p)cnt-=i/p; for(ll i=n-m;i;i=i/p)cnt-=i/p; ll ret=qpow(p,cnt,k)*fac(n,p,k)%k; ret=ret*inv(fac(m,p,k),p,t,k)%k; ret=ret*inv(fac(n-m,p,k),p,t,k)%k; return ret; } inline ll calc(ll p,ll t) { ll ret=1,tot=n,k=qpow(p,t); for(int pos=1;pos<=m;pos++) { ret=ret*c(tot,w[pos],p,t)%k; tot-=w[pos]; } return ret; } pr invv(ll x,ll y) { if(!y)return pr(1,0); pr tmp=invv(y,x%y); return pr(tmp.second,tmp.first-x/y*tmp.second); } inline ll crt(pr *aa,int n) { ll m=p,ret=0; for(int i=1;i<=n;i++) { ll x=aa[i].first,a=aa[i].second; ll mi=m/x,invs=(invv(mi,x).first%m+m)%m; ret=(ret+a*mi*invs)%m; } return ret; } inline ll work(ll p) { int top=0; for(ll i=2;i*i<=p;i++) { if(p%i==0) { int cnt=0; while(p%i==0) p/=i,cnt++; ans[++top]=pr(qpow(i,cnt),calc(i,cnt)); } } if(p!=1)ans[++top]=pr(p,calc(p,1)); return crt(ans,top); } int main() { p=read();n=read();m=read(); ll sum=0; for(int i=1;i<=m;i++) w[i]=read(),sum+=w[i]; if(sum>n) { puts("Impossible"); return 0; } if(n>sum)w[++m]=n-sum; printf("%lld\n",work(p)); return 0; }