BZOJ2142 禮物 EXlucas

全場最佳...毒瘤題


一眼得出公式:C(n,sum)*C(sum,a[1])*C(sum-a[1],a[2])*....

然而數據範圍n<=1e9


顯然可以用lucas定理算,但是模數tmd不一定是個質數

於是需要用到ex_lucas......


把p拆成若干個質因子冪乘積的形式,然後對於每一個質因子冪次求解,最後中國剩餘定理乘起來即可

具體過程:

1、拆

得到底數q與q^t,t表示q因子的數量

這種情況下

我們可以對C求解

原因是:這種情況下,我們只需要把所有q因子提出來,就可以求逆元

如何求解:

2、求 C(i,j)%(q^t)

我們可以吧含有q的項拆出來,方法如下:

例如q=3,t=2;

求20!在模域q^t下的值

20!=1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20

=1*2*4*5 * 7*8*10*11 * 13*14*16*17  * 19*20 *3^6*(1+2+3+4+5+6)

=(1*2*4*5)^3 *3^t *19*20 * 6!

於是我們可以對6!繼續此操作 最終求出20!

這個地方解釋一下爲什麼1*2*4*5=7*8*10*11

7*8*10*11=(1+6)*(2+6)*(4+6)*(5+6) 顯然,展開後只有1*2*4*5這一項不含因子6


於是我們就可以在log*某較大常數  的時間內求出k!的值了

然後暴力算一下i!中q的數量-j!和(i-j)!中q的數量,乘進答案裏

餘下的部分則可以用上述算法求出階乘後用exgcd求逆元,暴力算出組合數


3、中國剩餘定理

求關於方程

x%p1=a1

x%p2=a2

x%p3=a3

...

所有p都互質

的解

我們設π p=P

P/p[i]=V[i]

V[i]在p[i]模域下的逆元爲REV[i]

爲什麼這樣?

我們可以發現一個很有用的性質

REV[I]*V[i]這個東西

模p[i]=1;模p[j] (j!=i) =0

因爲V[i]是除了p[i]以外所有p的乘積

而REV是V[i]在p[i]意義下的逆元


所以我們的一個解ANS=sigma(a[i]*REV[i]*V[i])%P

其他解。。如果要用的話,ANS'=ANS+K*P;



以上就是ex_Lucas的所有要用到的東西了

CODE:

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#include<map>
#include<cstring>
#define For(i,j,k) for(ll i=j;i<=k;++i)
#define Dow(i,j,k) for(ll i=k;i>=j;--i)
#define pb push_back
#define inf 1e18
#define maxn 500001
#define ll long long
using namespace std;
inline ll read()
{
	ll t=0,f=1;char c=getchar();
	while(c<'0'||c>'9')	{if(c=='-')	f=-1;c=getchar();}
	while(c<='9'&&c>='0')	t=t*10LL+c-48LL,c=getchar();
	return t*f;
}
ll mo,n,m,sum,x[200001];
inline ll ksm(ll x,ll y,ll m){ll sum=1;for(;y;y/=2){if(y&1)	sum=sum*x%m;x=x*x%m;}	return sum;}
inline void exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)	{x=1;y=0;}
		else	exgcd(b,a%b,y,x),y-=(a/b)*x;
}
inline ll rev(ll x,ll pr)
{
	ll tx=0,ty=0;
	exgcd(x,pr,tx,ty);
	return (tx%pr+pr)%pr;
}
inline ll mul(ll x,ll p,ll pr)
{
	if(x==0)	return 1;
	ll tmp=1;
	For(i,1,pr)	if(i%p)	tmp*=i,tmp%=pr;
	tmp=ksm(tmp,x/pr,pr);
	ll tep=x%pr;
	For(i,2,tep)	if(i%p)	tmp*=i,tmp%=pr;
	tmp=tmp*mul(x/p,p,pr);
	return tmp%pr;	
}
inline ll crt(ll x,ll y,ll p,ll pr)
{
	ll t1=mul(x,p,pr),t2=mul(y,p,pr),t3=mul(x-y,p,pr);
	ll tot=0;
	ll tmp=x;while(tmp)	tot+=tmp/p,tmp/=p;
	tmp=y;while(tmp)	tot-=tmp/p,tmp/=p;
	tmp=x-y;while(tmp)	tot-=tmp/p,tmp/=p;
	ll ans=t1*rev(t2,pr)%pr*rev(t3,pr)%pr*ksm(p,tot,pr)%pr;
	return ans*(mo/pr)%mo*rev(mo/pr,pr)%mo;
}
inline ll lucas(int x,int y)
{
	ll tmp=mo,ans=0;
	For(i,2,tmp)
	{
		ll mul=1;
		while(tmp%i==0)	mul*=i,tmp/=i;
		if(mul!=1)	ans=(ans+crt(x,y,i,mul));	
	}
	return ans;
}
int main()
{
	mo=read();
	n=read();m=read();
	For(i,1,m)	x[i]=read(),sum+=x[i];
	if(sum>n)	{puts("Impossible");return 0;}
	ll ans=lucas(n,sum)%mo;
	For(i,1,m)
	{
		ans=ans*lucas(sum,x[i]),sum-=x[i],ans%=mo;
	}
	printf("%lld\n",ans);
}
優化後的EX_LUCAS
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#include<map>
#include<cstring>
#define For(i,j,k) for(ll i=j;i<=k;++i)
#define Dow(i,j,k) for(ll i=k;i>=j;--i)
#define pb push_back
#define inf 1e18
#define maxn 500001
#define ll long long
using namespace std;
inline ll read()
{
	ll t=0,f=1;char c=getchar();
	while(c<'0'||c>'9')	{if(c=='-')	f=-1;c=getchar();}
	while(c<='9'&&c>='0')	t=t*10LL+c-48LL,c=getchar();
	return t*f;
}
ll mo,n,m,sum,x[200001],rest,blo;
inline ll ksm(ll x,ll y,ll m){ll sum=1;for(;y;y/=2){if(y&1)	sum=sum*x%m;x=x*x%m;}	return sum;}
inline void exgcd(ll a,ll b,ll &x,ll &y)
{
	if(b==0)	{x=1;y=0;}
		else	exgcd(b,a%b,y,x),y-=(a/b)*x;
}
inline ll rev(ll x,ll pr)
{
	ll tx=0,ty=0;
	exgcd(x,pr,tx,ty);
	return (tx%pr+pr)%pr;
}
ll pre[2000001];
inline ll mul(ll x,ll p,ll pr)
{
	if(x==0)	return 1;
	ll tmp=pre[pr];
	tmp=ksm(tmp,x/pr,pr);
	tmp=tmp*pre[x%pr];
	tmp=tmp*mul(x/p,p,pr);
	return tmp%pr;	
}
inline ll crt(ll x,ll y,ll p,ll pr)
{
	pre[0]=1;
	For(i,1,pr) pre[i]=pre[i-1]*(i%p?i:1)%pr;	
	ll t1=mul(x,p,pr),t2=mul(y,p,pr),t3=mul(x-y,p,pr);
	ll tot=0;
	ll tmp=x;while(tmp)	tot+=tmp/p,tmp/=p;
	tmp=y;while(tmp)	tot-=tmp/p,tmp/=p;
	tmp=x-y;while(tmp)	tot-=tmp/p,tmp/=p;
	ll ans=t1*rev(t2,pr)%pr*rev(t3,pr)%pr*ksm(p,tot,pr)%pr;
	return ans*(mo/pr)%mo*rev(mo/pr,pr)%mo;
}
inline ll lucas(ll x,ll y)
{
	ll tmp=mo,ans=0;
	For(i,2,tmp)
	{
		ll mul=1;
		while(tmp%i==0)	mul*=i,tmp/=i;
		if(mul!=1)	ans=(ans+crt(x,y,i,mul))%mo;	
	}
	return ans;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章