51nod1486 大大走格子

n<=100000 * m<=100000的网格求不经过给定的h<=2000个点,从左上角到右下角只向右或向下走的方案数。

先来看不经过一个点:

要绕过这个点到右下角,可以先到该点右上角,再下来,或者到该点左下角,再往右。更一般的,是到该点右下角的总方案,减去经过该点的方案数。

如果这个点坐标(n,m),那么到右下角,方案数就是S(n+1,m+1)-S(n,m),其中S(i,j)=C(i+j,j),是到点(i,j)的所有方案数,自行推导。

那如果有多个点呢?我们需要不重复、不遗漏地统计“经过某个点的所有方案”。由于只向右向下,所以统计经过点(i,j)的方案时,必须保证不经过(x,y),x<=i,y<=j的所有点。

那么就按x排序再按y排序所有点,令f(i)表示到点i,不经过任何i左上方的点的方案数,则f(i)=S(Xi-1,Yi-1)-f(j)*S(Xi-Xj,Yi-Yj),其中Xj<=Xi,Yj<=Yi。先走合法方案到某个点,后面乱走到点i,这样统计方案一定不重复,因为f(j)表示的方案一定不含经过点k,Xk<=Xj,Yk<=Yj的方案,所以f(j)转移过来的方案都没经过他左上方的所有点,这样即使k的方案中可能有经过j,也不会重复。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<algorithm>
 5 //#include<assert.h>
 6 //#include<iostream>
 7 using namespace std;
 8 
 9 int n,m,h;
10 #define maxh 2011
11 int f[maxh];
12 struct Point
13 {
14     int x,y;
15     bool operator < (const Point &b) const
16     {return x<b.x || (x==b.x && y<b.y);}
17 }a[maxh];
18 #define maxn 200011
19 int fac[maxn],inv[maxn];
20 const int mod=1e9+7;
21 int powmod(int a,int b)
22 {
23     int ans=1;
24     for (;b;a=1ll*a*a%mod,b>>=1) if (b&1) ans=1ll*ans*a%mod;
25     return ans;
26 }
27 void pre(int n)
28 {
29     fac[0]=1;for (int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mod;
30     inv[n]=powmod(fac[n],mod-2);for (int i=n-1;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
31 }
32 int C(int n,int m) {return 1ll*fac[n]*inv[n-m]%mod*inv[m]%mod;}
33 int S(int n,int m) {return C(n+m,n);}
34 int main()
35 {
36     scanf("%d%d%d",&n,&m,&h);
37     for (int i=1;i<=h;i++) scanf("%d%d",&a[i].x,&a[i].y);
38     sort(a+1,a+1+h);
39     a[++h].x=n;a[h].y=m;
40     pre(n+m+2);
41     f[1]=S(a[1].x-1,a[1].y-1);
42     for (int i=2;i<=h;i++)
43     {
44         f[i]=S(a[i].x-1,a[i].y-1);
45         for (int j=1;j<i;j++)
46             if (a[i].y>=a[j].y) f[i]-=1ll*f[j]*S(a[i].x-a[j].x,a[i].y-a[j].y)%mod,
47             f[i]+=f[i]<0?mod:0;
48     }
49     printf("%d
",f[h]);
50     return 0;
51 }
View Code
原文地址:https://www.cnblogs.com/Blue233333/p/7570650.html