ZOJ 3557 How Many Sets II
Given a set S = {1, 2, ..., n}, number m and p, your job is to count how many set T satisfies the following condition:
- T is a subset of S
- |T| = m
- T does not contain continuous numbers, that is to say x and x+1 can not both in T
Input
There are multiple cases, each contains 3 integers n ( 1 <= n <= 109 ), m ( 0 <= m <= 104, m <= n ) and p ( p is prime, 1 <= p <= 109 ) in one line seperated by a single space, proceed to the end of file.
Output
Output the total number mod p.
Sample Input
5 1 11 5 2 11
Sample Output
5 6
题意:求从n个元素里选m个互不相邻的元素的方案数
选出m个,剩余n-m个,有n-m+1个空位,ans=将这m个元素放到这n-m+1个空位,每个空位只能放一个的方案数
即在n-m+1个空位里选m个的方案数
C(n-m+1,m)
Lucas定理,但p很大,所以不能预处理阶乘
m比较小,所以在计算的时候,C(n,m)转化成 C(n,n-m),这样计算C只需要枚举到m
扩展欧几里得求逆元 比 费马小定理 要快一点儿
#include<cstdio> using namespace std; typedef long long LL; /*int pow(LL a,int b,int p) { LL r=1; while(b) { if(b&1) r*=a,r%=p; b>>=1; a*=a; a%=p; } return r; }*/ void exgcd(int a,int b,LL &x,LL &y) { if(!b) { x=1; y=0; } else { exgcd(b,a%b,y,x); y-=x*(a/b); } } int inv(int x,int p) { //return pow(x,p-2,p); LL x0,y0; exgcd(x,p,x0,y0); return (x0%p+p)%p; } int C(int n,int m,int p) { if(n<m) return 0; LL r=1; //for(int i=m+1;i<=n;i++) r=r*i%p*inv(i-m,p)%p; TLE for(int i=1;i<=m;i++) r=r*(n-i+1)%p*inv(i,p)%p; return r; } int Lucas(int n,int m,int p) { LL ans=1; for(;m;n/=p,m/=p) ans=ans*C(n%p,m%p,p)%p; return ans; } int main() { int n,m,p; while(scanf("%d%d%d",&n,&m,&p)!=EOF) printf("%d\n",Lucas(n-m+1,m,p)); }