zoukankan      html  css  js  c++  java
  • bzoj2142: 礼物

    2142: 礼物

    Description

    一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。小E从商店中购买了n件礼物,打算送给m个人,其中送给第i个人礼物数量为wi。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模P后的结果。
    

    Input

    输入的第一行包含一个正整数P,表示模;
    第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;
    以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。
    

    Output

    若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。
    

    数据规模和约定

    设 $ P = \prod_{i=1}^n p_i^{c_i} $ 且 $ p_i $ 为质数

    $1 \leqslant n \leqslant 10^9,1 \leqslant m \leqslant 5,1 \leqslant p_i^{c_i} \leqslant 10^5 $。

    拓展卢卡斯和中国剩余定理的裸题
    题目可以转换为求

    \[\prod_{i=1}^n C_{n- \sum_{j=0}^{i-1} w[j]}^{w[i]} \]

    剩下的就是求 $ C_n^m % p $ 的问题了。

    因为n,m,p都很大,所以我们可以使用拓展卢卡斯求出对于每个 $ p_i^{c_i} $ 余数,然后通过中国剩余定理合并即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i)
    #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i)
    #define pii pair<ll,ll> 
    typedef long long ll;
    inline int read(){
    	int x;
    	char c;
    	int f=1;
    	while((c=getchar())!='-' && (c<'0' || c>'9'));
    	if(c=='-') c=getchar(),f=-1;
    	x=c^'0';
    	while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0');
    	return x*f;
    }
    inline ll readll(){
    	ll x;
    	char c;
    	ll f=1;
    	while((c=getchar())!='-' && (c<'0' || c>'9'));
    	if(c=='-') c=getchar(),f=-1;
    	x=c^'0';
    	while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0');
    	return x*f;
    }
    vector<pii> vc;
    ll ksm(ll x,ll y,ll mod){
    	ll res=1;
    	while(y){
    		if(y&1ll) res=res*x%mod;
    		x=x*x%mod;
    		y>>=1ll;
    	}
    	return res;
    }
    ll exgcd(ll &x,ll &y,ll a,ll b){
    	if(!b){
    		x=1,y=0;
    		return a;
    	}
    	ll res=exgcd(y,x,b,a%b);
    	y-=a/b*x;
    	return res;
    }
    ll inv(ll a,ll b){
    	a%=b;
    	ll x,y;
    	exgcd(x,y,a,b);
    	x=(x%b+b)%b;
    	if(!x) x+=b;
    	return x;
    }
    ll fac(ll x,ll u,ll mod){
    	if(!x || x==1) return 1;
    	ll ans=1,num=1;
    	if(x/mod){
    		for(ll i=2;i<mod;++i)
    			if(i%u) num=num*i%mod;
    		ans=ans*ksm(num,x/mod,mod);
    	}
    	ans=ans*fac(x/u,u,mod)%mod;
    	x%=mod;
    	for(ll i=2;i<=x;++i)
    		if(i%u) ans=ans*i%mod;
    	return ans;
    }
    ll calc(ll x,ll u){
    	ll res=0;
    	for(;x;x/=u) res+=x/u;
    	return res;
    }
    ll C(ll n,ll m,ll u,ll v){
    	ll x=fac(n,u,v),y=fac(m,u,v),z=fac(n-m,u,v);
    //	cout<<n<<' '<<m<<' '<<v<<' '<<x<<' '<<y<<' '<<z<<endl;
    	ll num=calc(n,u)-calc(m,u)-calc(n-m,u);
    	x=x*inv(y,v)%v*inv(z,v)%v;
    	return x*ksm(u,num,v)%v;
    }
    ll exLucas(ll n,ll m,ll mod){
    	if(m>n) return 0;
    	ll ans=0;
    	REP(i,0,vc.size()-1){
    		pii u=vc[i];
    		ll x=u.first,y=u.second;
    		ans=(ans+inv(mod/y,y)*(mod/y)%mod*C(n,m,x,y)%mod)%mod;
    	}
    	return ans;
    }
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("china.in","r",stdin);
    	freopen("china.out","w",stdout);
    #endif
    	ll mod=readll(),n=readll(),ans=1;
    	int m=read(),u=mod;
    	for(ll i=2;i*i<=u;++i){
    		if(u%i) continue;
    		ll x=1;
    		while(u%i==0) u/=i,x*=i;
    		vc.push_back(make_pair(i,x));
    //		cout<<i<<' '<<x<<endl;
    	}
    	if(u!=1) vc.push_back(make_pair(u,u));
    //	cout<<u<<endl;
    	REP(i,1,m){
    		ll x=readll();
    		ans=ans*exLucas(n,x,mod)%mod;
    		if(!ans){
    			printf("Impossible\n");
    			return 0;
    		}
    		n-=x;
    	}
    	printf("%lld\n",ans);
    	return 0;
    }
    
  • 相关阅读:
    PMP CMM
    PM过程的一些典型场景和问题
    PMP的六大管理学定律
    项目经理面试指南
    Sd
    Java 对象池实现
    Java 线程池的实现
    Sd
    Sd
    02.JSP的3个编译指令
  • 原文地址:https://www.cnblogs.com/zhou888/p/8479711.html
Copyright © 2011-2022 走看看