C(n,m)%P
Lucas定理
“卢卡斯定理:C(n,m)=C(n%p,m%p)*C(n/p,m/p) mod p 要求p是质数
其中n%p可能会小于m%p 这种情况下直接返回0即可
证明去问卢卡斯 我不知道”
——PoPoQQQ爷
http://blog.csdn.net/popoqqq/article/details/40507373
中国剩余定理
”题目大意:给定n个物品,分给m个人,每个人拿到wi个礼物,问方案数mod P P不一定为质数
首先我们把剩下的礼物也分给一个人 答案明显不变 w[++m]=n-w1-w2-...-wm
然后就会很方便地得到公式:
ans=C(n,w1)*C(n-w1,w2)*C(n-w1-w2,w3)*...*C(n-w1-w2-...-w_(m-1),wm) mod P
=n!/w1!/w2!/.../wm! mod P
然后p不是质数 我们把P分解 令P=∏pi^ai
我们分别处理,可以得到一次同余方程组ans%pi^ai=lefti,用中国剩余定理合并一下即可。
然后对于每个pi^ai,我们进行以下处理:
将分子和分母化为x*pi^y的形式
然后分母的x部分与pi互质,可以求逆元,分子分母的y部分直接相减即可
然后我们处理阶乘
以19为例,将19!化为x*pi^y的形式,其中pi=3,ai=2 则有
19!%9=(1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19) %9
=(1*2*4*5*7*8*10*11*13*14*16*17*19)*3^6*(1*2*3*4*5*6) %9
式子的左半部分是不为3的倍数的数,存在长度为p^a的循环节 求出一个循环节 快速幂处理 然后处理剩余部分
右半部分是6!%9 递归处理即可
我这题解写的真是冷静。。。这题还真TM让人冷静不下来啊-0-“
——PoPoQQQ爷
http://blog.csdn.net/popoqqq/article/details/39891263
19!%9 这个例子特别好
BZOJ2142
n!/w1!/w2!/.../wm! mod P
然后中国剩余定理合并
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> data;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x)
{
char c=nc(),b=1;
for (; !(c>='0' && c<='9'); c=nc()) if (c=='-') b=-1;
for (x=0; c>='0' && c<='9'; x=x*10+c-'0',c=nc()); x*=b;
}
inline ll Mul(ll a,ll b,ll p)
{
ll ret=0;
for (;b;b>>=1,a=(a<<1)%p)
if (b&1)
(ret+=a)%=p;
return ret;
}
inline ll Pow(ll a,ll b,ll p)
{
if (b==0) return 1LL;
ll ret=Pow(a,b>>1,p);
if (b&1) return ret*ret%p*a%p;
return ret*ret%p;
}
inline ll EXGCD(ll a,ll b,ll& x,ll &y) //a>=b
{
if (a<b) return EXGCD(b,a,y,x);
if (b==0) { x=1; y=0; return a; }
ll d=EXGCD(b,a%b,x,y);
ll tmp=y; y=x-a/b*y; x=tmp;
return d;
}
inline ll Reverse(ll a,ll p)
{
ll X,Y; EXGCD(a,p,X,Y); return (X%p+p)%p;
}
namespace China {
ll A[100005],N[100005],C[100005];
int tot=0;
ll P=1;
inline void add(ll a,ll n) {
A[++tot]=a; N[tot]=n; P*=n;
}
inline ll solve() {
ll X,Y,ret=0,tmp;
for (int i=1; i<=tot; i++) {
C[i]=P/N[i];
X=Reverse(C[i],N[i]);
(ret+=Mul(Mul(A[i],C[i],P),X,P))%=P;
}
return ret;
}
}
int vst[100005],prime[100005],num;
inline void Pre()
{
const int maxn=100000;
for (int i=2; i<=maxn; i++)
if (!vst[i])
{
prime[++num]=i;
for (int j=i+i; j<=maxn; j+=i) vst[j]=1;
}
}
ll M,ans;
ll p[50005],a[50005],p_a[50005],cnt;
ll n,m,w[10];
inline void Machine(ll M)
{
for (int i=1; i<=num; i++)
if (M%prime[i]==0) {
p[++cnt]=prime[i];
while (M%prime[i]==0) a[cnt]++,M/=prime[i];
p_a[cnt]=Pow(p[cnt],a[cnt],1000000);
}
}
inline data Calc(ll x,ll p,ll a,ll p_a)
{
ll A=1,B=1;
for (ll i=1;i<p_a;i++)
if (i%p)
(A*=i)%=p_a;
A=Pow(A,x/p_a,p_a);
for (ll i=x-x%p_a+1;i<=x;i++)
if (i%p)
(A*=i)%=p_a;
B=x/p;
if (B)
{
data tmp=Calc(B,p,a,p_a);
(A*=tmp.first)%=p_a;
B+=tmp.second;
}
return make_pair(A,B);
}
int main() {
Pre();
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(M); Machine(M);
read(n); read(m);
ll sum=0;
for (int i=1; i<=m; i++) read(w[i]),sum+=w[i];
if (sum>n) printf("Impossible\n"),exit(0);
if (n-sum) w[++m]=n-sum;
for (int i=1;i<=cnt;i++)
{
data ret=Calc(n,p[i],a[i],p_a[i]);
for (int j=1;j<=m;j++)
{
data tmp=Calc(w[j],p[i],a[i],p_a[i]);
ret.second-=tmp.second;
(ret.first*=Reverse(tmp.first,p_a[i]))%=p_a[i];
}
(ret.first*=Pow(p[i],ret.second,p_a[i]))%=p_a[i];
China::add(ret.first,p_a[i]);
}
ans=China::solve();
cout<<ans<<endl;
return 0;
}
BZOJ3738
去0时记得乘上2,5的逆元
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<iomanip>
#define P1 512ll
#define P2 1953125ll
using namespace std;
typedef long long ll;
typedef pair<ll,ll> data;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x)
{
char c=nc(),b=1;
for (; !(c>='0' && c<='9'); c=nc()) if (c=='-') b=-1;
for (x=0; c>='0' && c<='9'; x=x*10+c-'0',c=nc()); x*=b;
}
inline ll Pow(ll a,ll b,ll p)
{
if (b==0) return 1LL;
ll ret=Pow(a,b>>1,p);
if (b&1) return ret*ret%p*a%p;
return ret*ret%p;
}
inline data Plus(data x,data y,ll p)
{
return make_pair(x.first*y.first%p,x.second+y.second);
}
inline data Minus(data x,data y,ll p,ll phi_p)
{
return make_pair(x.first*Pow(y.first,phi_p-1,p)%p,x.second-y.second);
}
inline data Find(ll x,ll p,ll p_a,ll* pow)
{
if (!x) return make_pair(1,0);
ll tmp=Pow(pow[p_a],x/p_a,p_a)*pow[x%p_a]%p_a;
return Plus(make_pair(tmp,x/p),Find(x/p,p,p_a,pow),p_a);
}
ll n,m,K,ans;
ll pow1[P1+1],pow2[P2+1];
int main() {
data ret1,ret2;
ll X,Y,tmp;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
pow1[0]=1; pow2[0]=1;
for (int i=1;i<=P1;i++) pow1[i]=pow1[i-1]*(i%2?i:1)%P1;
for (int i=1;i<=P2;i++) pow2[i]=pow2[i-1]*(i%5?i:1)%P2;
read(n); read(m); read(K);
ret1=Find(n+m,2,P1,pow1);
ret1=Minus(ret1,Find(n,2,P1,pow1),P1,P1/2*1);
ret1=Minus(ret1,Find(m,2,P1,pow1),P1,P1/2*1);
ret2=Find(n+m,5,P2,pow2);
ret2=Minus(ret2,Find(n,5,P2,pow2),P2,P2/5*4);
ret2=Minus(ret2,Find(m,5,P2,pow2),P2,P2/5*4);
tmp=min(ret1.second,ret2.second);
X=ret1.first*Pow(2,ret1.second-tmp,P1)%P1*Pow(205,tmp,P1)%P1;
Y=ret2.first*Pow(5,ret2.second-tmp,P2)%P2*Pow(976563,tmp,P2)%P2;
ans=(1953125*109*X+512*1537323*Y)%1000000000;
cout<<setfill('0')<<setw(K)<<ans%Pow(10,K,1000000001)<<endl;
return 0;
}
BZOJ2982
Lucas定理
逆元预处理 (inv[i]=(P-P/i)*inv[P%i])%=P
#include<cstdio>
#include<cstdlib>
#define M 10007
using namespace std;
typedef long long ll;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void write(ll x,char c)
{
if (x==0) { putchar('0'); putchar(c); return; }
if (x<0) putchar('-'),x=-x;
char s[50]={0},len=0;
while (x) s[++len]=x%10+'0',x/=10;
for (int i=len;i;i--) putchar(s[i]);
putchar(c);
}
ll fac[10010],inv[10010];
inline void Pre()
{
fac[0]=1;
for (int i=1;i<M;i++)
fac[i]=fac[i-1]*i%M;
inv[1]=1;
for (int i=2;i<M;i++)
inv[i]=(M-M/i)*inv[M%i]%M;
inv[0]=1;
for (int i=1;i<M;i++)
(inv[i]*=inv[i-1])%=M;
}
inline ll C(ll n,ll m)
{
if (m>n) return 0LL;
if (n<M && m<M)
return fac[n]*inv[m]%M*inv[n-m]%M;
return C(n/M,m/M)*C(n%M,m%M)%M;
}
int main()
{
Pre();
ll Q,n,m;
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(Q);
while (Q--)
{
read(n); read(m);
write(C(n,m),'\n');
}
return 0;
}
BZOJ1951
还得用上费马小定理
“注意此题有个细节 就是欧拉定理中a与p必须互质 而当a=0(即G=p)时gcd(a,p)=p
所以有一组数据是专门卡这个地方的 这组数据是999911657 999911659 取模后是0^0 于是得到1 但是答案是0 ” ——PoPoQQQ爷
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#define M 999911659LL
using namespace std;
typedef long long ll;
typedef pair<ll,ll> data;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
ll prime[4]={2,3,4679,35617};
ll fac[4][36000],inv[4][36000];
inline void Pre()
{
for (int i=0;i<4;i++)
{
ll P=prime[i];
fac[i][0]=1;
for (int j=1;j<=P;j++)
(fac[i][j]=j*fac[i][j-1])%=P;
inv[i][1]=1;
for (int j=2;j<=P;j++)
(inv[i][j]=(P-P/j)*inv[i][P%j])%=P;
inv[i][0]=1;
for (int j=1;j<=P;j++)
(inv[i][j]*=inv[i][j-1])%=P;
}
}
inline ll C(ll n,ll m,ll P,int t)
{
if (n<m) return 0LL;
if (n<P && m<P)
return fac[t][n]*inv[t][m]%P*inv[t][n-m]%P;
return C(n/P,m/P,P,t)*C(n%P,m%P,P,t)%P;
}
inline data EXGCD(ll a,ll b)
{
if (a<b) { data tmp=EXGCD(b,a); return data(tmp.second,tmp.first); }
if (b==0) return data(1,0);
data tmp=EXGCD(b,a%b);
return data(tmp.second,tmp.first-a/b*tmp.second);
}
inline ll Chinese(ll *A,ll *N,ll P,int cnt)
{
ll ret=0,Mi,Ci;
for (int i=0;i<cnt;i++)
{
Mi=P/N[i];
Ci=(EXGCD(Mi,N[i]).first%P*Mi%P+P)%P;
(ret+=Ci*A[i]%P)%=P;
}
return ret;
}
inline ll Pow(ll a,ll b,ll p)
{
if (b==0) return 1LL;
ll ret=Pow(a,b>>1,p);
if (b&1) return ret*ret%p*a%p;
return ret*ret%p;
}
ll N,G,Ans;
ll ans[4];
inline void Calc(ll x)
{
for (int i=0;i<4;i++)
(ans[i]+=C(N,x,prime[i],i))%=prime[i];
}
int main()
{
Pre();
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(N); read(G);
for (int i=1;i*i<=N;i++)
if (i*i==N)
Calc(i);
else if (N%i==0)
Calc(i),Calc(N/i);
Ans=Chinese(ans,prime,M-1,4);
Ans=Pow(G%M,Ans+M-1,M);
cout<<Ans<<endl;
}
BZOJ3782
组合数dp
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#define X first
#define Y second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> data;
typedef pair<ll,ll> Poi;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
namespace Deal1{
#define M 1019663265LL
ll prime[4]={3,5,6793,10007};
ll fac[4][36000],inv[4][36000];
inline void Pre()
{
for (int i=0;i<4;i++)
{
ll P=prime[i];
fac[i][0]=1;
for (int j=1;j<=P;j++)
(fac[i][j]=j*fac[i][j-1])%=P;
inv[i][1]=1;
for (int j=2;j<=P;j++)
(inv[i][j]=(P-P/j)*inv[i][P%j])%=P;
inv[i][0]=1;
for (int j=1;j<=P;j++)
(inv[i][j]*=inv[i][j-1])%=P;
}
}
inline ll C(ll n,ll m,ll P,int t)
{
if (n<m) return 0LL;
if (n<P && m<P)
return fac[t][n]*inv[t][m]%P*inv[t][n-m]%P;
return C(n/P,m/P,P,t)*C(n%P,m%P,P,t)%P;
}
inline data EXGCD(ll a,ll b)
{
if (b==0) return data(1,0);
data tmp=EXGCD(b,a%b);
return data(tmp.second,tmp.first-a/b*tmp.second);
}
inline ll Chinese(ll *A,ll *N,ll P,int cnt)
{
ll ret=0,Mi,Ci;
for (int i=0;i<cnt;i++)
{
Mi=P/N[i];
Ci=(EXGCD(Mi%N[i],N[i]).first%P*Mi%P+P)%P;
(ret+=Ci*A[i]%P)%=P;
}
return ret;
}
ll Ans,ans[4];
inline void Calc(ll n,ll m)
{
for (int i=0;i<4;i++)
(ans[i]=C(n,m,prime[i],i))%=prime[i];
}
inline ll Solve(ll n,ll m)
{
Calc(n,m);
Ans=Chinese(ans,prime,M,4);
return Ans;
}
#undef M
}
namespace Deal2{
ll P=1000003;
ll fac[1000005],inv[1000005];
inline void Pre()
{
fac[0]=1;
for (int j=1;j<=P;j++)
(fac[j]=j*fac[j-1])%=P;
inv[1]=1;
for (int j=2;j<=P;j++)
(inv[j]=(P-P/j)*inv[P%j])%=P;
inv[0]=1;
for (int j=1;j<=P;j++)
(inv[j]*=inv[j-1])%=P;
}
inline ll C(ll n,ll m)
{
if (n<m) return 0LL;
if (n<P && m<P)
return fac[n]*inv[m]%P*inv[n-m]%P;
return C(n/P,m/P)*C(n%P,m%P)%P;
}
inline ll Solve(ll n,ll m)
{
return C(n,m);
}
}
ll N,M,T,P;
Poi point[205];
ll f[205];
inline ll CC(ll n,ll m)
{
ll ret;
if (P==1000003)
ret=Deal2::Solve(n,m);
else
ret=Deal1::Solve(n,m);
return ret;
}
int main()
{
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(N); read(M); read(T); read(P);
if (P==1000003) Deal2::Pre(); else Deal1::Pre();
for (int i=1;i<=T;i++)
read(point[i].X),read(point[i].Y);
point[++T]=Poi(N,M);
sort(point+1,point+T+1);
for (int i=1;i<=T;i++)
{
f[i]=CC(point[i].X+point[i].Y,point[i].X);
for (int j=1;j<i;j++)
if (point[i].Y>=point[j].Y)
((f[i]-=f[j]*CC((point[i].X-point[j].X)+(point[i].Y-point[j].Y),(point[i].X-point[j].X))%P)+=P)%=P;
}
cout<<f[T]<<endl;
return 0;
}
BZOJ2111
一开始没看出是个小根堆。。组合数dp
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef long long ll;
inline char nc()
{
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(ll &x)
{
char c=nc(),b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
ll n,P;
ll fac[1000005],inv[1000005];
ll f[2000005],size[2000005];
inline void Pre()
{
fac[0]=1;
for (int i=1;i<=n && i<P;i++)
(fac[i]=fac[i-1]*i)%=P;
inv[1]=1;
for (int i=2;i<=n && i<P;i++)
(inv[i]=(P-P/i)*inv[P%i])%=P;
inv[0]=1;
for (int i=1;i<=n && i<P;i++)
(inv[i]*=inv[i-1])%=P;
}
inline ll C(ll n,ll m)
{
if (n<m) return 0LL;
if (n<P && m<P)
return fac[n]*inv[m]%P*inv[n-m]%P;
return C(n/P,m/P)*C(n%P,m%P)%P;
}
int main()
{
freopen("t.in","r",stdin);
freopen("t.out","w",stdout);
read(n); read(P);
Pre();
for (int i=n+1;i<=(n<<1|1);i++)
f[i]=1;
for (int i=n;i;i--)
{
size[i]=size[i<<1]+size[i<<1|1]+1;
f[i]=C(size[i]-1,size[i<<1])*f[i<<1]%P*f[i<<1|1]%P;
}
cout<<f[1]<<endl;
}