首页 > 代码库 > 【算法】卢卡斯定理

【算法】卢卡斯定理

【问题描述】

OI大师抖儿在夺得银牌之后,顺利保送pku。这一天,抖儿问长者:“虽然我已经保送了,但是我还要参加学考。马上就要考政治了,请问应该怎样学习哲学,通过政治考试?”
长者回答:“你啊,Too Young Too Simple,Sometimes Naive!哲学这种东西,不是说想懂就能懂的,需要静心撕烤。你去后面的森林里好好想想。”

长者的后院有一片哲♂学森林。由于一些奥妙重重的原因,这片森林构成了一个n*m的矩形,其中每个点就代表了一棵树。此外,由于辣鸡出题人KJDH从中捣鬼,有些树被连根拔起(也就是消失了)。抖儿每天都要到树下撕烤,因此他想要在每一行选择一棵树。但是他非常讨厌走回头路,因此第i行选择的树必须比第i-1行的靠右。现在抖儿想知道,总共有多少种选择的方案。

【输入格式】

第一行三个整数n,m,p,分别表示森林的长、宽,以及消失的树的数目。

接下来p行每行两个整数,表示第ai行第bi列的树消失了。

p<=2000 n,m<=10^9

【输出格式】

一行一个整数,表示方案数。由于答案可能很大,请对1000003取模。

【题解】

C(n,m)%p=C(n%p,M%p)*C(n/p,m/p)%p

显然向下x个单位,向右y个方案是C(x-1,y-1)

f[i]只到第i个点的方案数

f[i]=C(x[i]-1,y[i]-1)-sigma(f[j]*C(x[i]-x[j]-1,y[i]-y[j]-1))(y[i]>=y[j]&&x[i]>=x[j]&&i>j)

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define il inline
#define re register
#define mod 1000003
using namespace std;
typedef long long ll;
const int N=2016;
int n,a[1100000],r[1100000],m,p,f[N];
struct data{int x,y;} t[N];
il bool cmp(data a,data b){
    return a.x<b.x;
}
il int C(int n,int m){
    if(n<m) return 0;
    if(n<mod) return 1ll*a[n]*r[m]*r[n-m]%mod;
    return 1ll*C(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
int main(){
    freopen("zhexue.in","r",stdin);
    freopen("zhexue.out","w",stdout);
    a[0]=1;r[1]=1;r[0]=1;
    for(int i=1;i<mod;i++) a[i]=1ll*a[i-1]*i%mod;
    for(int i=2;i<mod;i++) r[i]=1ll*r[mod%i]*(mod-mod/i)%mod;
    for(int i=1;i<mod;i++) r[i]=1ll*r[i]*r[i-1]%mod;
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1;i<=p;i++)
        scanf("%d%d",&t[i].x,&t[i].y);
    t[++p].x=n+1;t[p].y=m+1;
    sort(t+1,t+p+1,cmp);
    for(int i=1;i<=p;i++){
        f[i]=C(t[i].y-1,t[i].x-1);
        //cout<<f[i]<<endl;
        for(int j=1;j<i;j++)
            if(t[j].x<t[i].x&&t[j].y<t[i].y){
                f[i]=((f[i]-1ll*f[j]*C(t[i].y-t[j].y-1,t[i].x-t[j].x-1)%mod)%mod+mod)%mod;
            }
    }
    cout<<f[p];
    return 0;
}

 

【算法】卢卡斯定理