[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坑了整整兩個小時
(╯‵□′)╯︵┻━┻
思路:
首先可以得出求答案的式子:
拆式子化簡?
確實可以拆,然而完全不需要。
考慮到模數不為質數,將模數拆成若干個質數的次冪分開計算,最後使用中國剩餘定理合併答案。
考慮到模數依舊不是質數而是質數的某個次冪,採用擴充套件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;
}