ZOJ 3557 How Many Sets II

How Many Sets II
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4536

Time Limit: 2 Seconds      Memory Limit: 65536 KB

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
",Lucas(n-m+1,m,p));
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7360119.html