[入门OJ3876]怎样学习哲学
题目大意:
有一个$n\times m(n,m\leq 10^9)$的网格图,从一个点可以到下一行中列数比它大的点。有$k(k\leq 2000)$个点是不能走的,问从第$1$行到第$n$行共有几种方案。
思路:
动态规划求出以点$i$为终点的方案数,直接$O(nm)$推显然会超时,因此我们$O(k)$可以对于每个障碍点求出组合数。组合数可以用Lucas定理求。
Lucas定理:$\binom{n}{m}\mod p=\binom{\lfloor\frac{n}{p}\rfloor}{\lfloor\frac{m}{p}\rfloor}\binom{n\mod p}{m\mod p}\mod p$。
1 #include<cstdio> 2 #include<cctype> 3 #include<vector> 4 #include<algorithm> 5 typedef long long int64; 6 inline int getint() { 7 register char ch; 8 while(!isdigit(ch=getchar())); 9 register int x=ch^'0'; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 11 return x; 12 } 13 const int mod=1000003,K=2001; 14 int fact[mod],factinv[mod],f[K]; 15 std::pair<int,int> v[K]; 16 void exgcd(const int &a,const int &b,int &x,int &y) { 17 if(!b) { 18 x=1; 19 y=0; 20 return; 21 } 22 exgcd(b,a%b,y,x); 23 y-=a/b*x; 24 } 25 inline int inv(const int &x) { 26 int ret,tmp; 27 exgcd(x,mod,ret,tmp); 28 return (ret%mod+mod)%mod; 29 } 30 int lucas(const int &n,const int &m) { 31 if(n<m) return 0; 32 if(n<mod&&m<mod) return (int64)fact[n]*factinv[m]%mod*factinv[n-m]%mod; 33 return (int64)lucas(n/mod,m/mod)*lucas(n%mod,m%mod)%mod; 34 } 35 int main() { 36 fact[0]=1; 37 for(register int i=1;i<mod;i++) { 38 fact[i]=(int64)fact[i-1]*i%mod; 39 } 40 factinv[mod-1]=inv(fact[mod-1]); 41 for(register int i=mod-2;~i;i--) { 42 factinv[i]=(int64)factinv[i+1]*(i+1)%mod; 43 } 44 const int n=getint(),m=getint(),k=getint(); 45 for(register int i=0;i<k;i++) { 46 const int x=getint(),y=getint(); 47 v[i]=std::make_pair(x,y); 48 } 49 std::sort(&v[0],&v[k]); 50 v[k]=std::make_pair(n+1,m+1); 51 for(register int i=0;i<=k;i++) { 52 f[i]=lucas(v[i].second-1,v[i].first-1); 53 for(register int j=0;j<i;j++) { 54 if(v[i].first<=v[j].first||v[i].second<=v[j].second) continue; 55 f[i]=((f[i]-(int64)f[j]*lucas(v[i].second-v[j].second-1,v[i].first-v[j].first-1))%mod+mod)%mod; 56 } 57 } 58 printf("%d\n",f[k]); 59 return 0; 60 }