题目链接在本地……
这题题意就是一个二维图,按象棋中马的走位从左上走到右下,中间有一些点不能到达,问有多少条路径。
可以很明显的想到是一个容斥的问题,但是如此众多的点,放在一起容斥属实非常复杂。在这种情况下,一般都要想到由简入繁:想到一个个的把点加进去看。
现将点都排一遍序,从左上到右下,开一个数组f[i]记录到当前点的路径上,不经过前面任何一个不能到的点的路径条数。
那么对于一个新的点x,结果 f[x] 就是从左上角到 x 的路径数减去所有 x 前面的点 j 到 x 的路径条数乘上 f[j]。因为 f 中存放的路径是干净的,不经过任何先前的不能到的点,所以这个不会减去重复的路径。这种容斥的思想值得学习。
另一个值得学习的就是遇到很大的组合数时,要想到Lucas定理,这个可以快速的计算longlong范围的组合数。所以要顺便学一下线性求阶乘逆元的方法。要学习的东西有很多!!
1 #include "bits/stdc++.h" 2 using namespace std; 3 typedef long long LL; 4 const int MAX=115; 5 const int MOD=110119; 6 LL t; 7 LL n,m,r,f[MAX]; 8 LL fac[MOD],inv[MOD],facinv[MOD]; 9 struct Node{ 10 LL x,y; 11 bool operator < (const Node &tt) const{ 12 if (x!=tt.x) return x<tt.x; 13 return y<tt.y; 14 } 15 }a[MAX]; 16 void init(){ 17 LL i,j; 18 memset(fac,0,sizeof(fac)); 19 memset(inv,0,sizeof(inv)); 20 memset(facinv,0,sizeof(facinv)); 21 fac[0]=fac[1]=inv[1]=facinv[0]=facinv[1]=1; 22 for (i=2;i<MOD;i++){ 23 fac[i]=fac[i-1]*i%MOD; 24 inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD; 25 facinv[i]=facinv[i-1]*inv[i]%MOD; 26 } 27 } 28 LL C(LL n, LL m){ 29 if(n<0 || m<0 || m>n) return 0; 30 return fac[n] * facinv[m] % MOD * facinv[n - m] % MOD; 31 } 32 33 LL Lucas(LL n, LL m){ 34 if(n<0 || m<0 || m>n) return 0; 35 LL res = 1; 36 while(n || m) { 37 res = res * C(n % MOD, m % MOD) % MOD; 38 n/=MOD; 39 m/=MOD; 40 } 41 return res; 42 } 43 int main(){ 44 // freopen ("l.in","r",stdin); 45 // freopen ("l.out","w",stdout); 46 LL i,j,zt1,zt2,step; 47 scanf("%lld",&t); 48 init(); 49 while (t--){ 50 scanf("%lld%lld%lld",&n,&m,&r); 51 for (i=1;i<=r;i++) 52 scanf("%lld%lld",&a[i].x,&a[i].y); 53 a[r+1].x=a[r+1].y=1; 54 a[r+2].x=n,a[r+2].y=m; 55 sort(a+1,a+r+3); 56 memset(f,0,sizeof(f)); 57 f[1]=-1+MOD; 58 for (i=2;i<=r+2;i++){ 59 for (j=1;j<i;j++){ 60 if (a[i].x < a[j].x || a[i].y < a[j].y || (a[i].x-a[j].x+a[i].y-a[j].y)%3 != 0 ) continue; 61 step=(a[i].x-a[j].x+a[i].y-a[j].y)/3; 62 zt1=a[i].x-a[j].x-step; 63 zt2=a[i].y-a[j].y-step; 64 f[i]=(f[i]+MOD-f[j]*Lucas(zt1+zt2,zt1)%MOD)%MOD; 65 } 66 } 67 printf("%lld\n",f[r+2]); 68 } 69 return 0; 70 }