给定一个坐标系,求从点$(1,1)$到点$(n,m)$有多少种方案数。 其中有$k$个点不能经过,会给定这$k$个点的坐标。
读入: 第一行:$n$,$m$,$k$ 接下来$k$行,每行两个整数,表示不能经过的点的坐标
输出:从$(1,1)$到(n,m)的方案数,对$10^9$+$7$取模($n,m \le 10^5$,$k \le 2*10^3$)
很有意思的一道计数题
首先我们可以发现,在没有任何限制的情况下,从$(1,1)$走到$(n,m)$的方案数为$C(n+m-2,m-1)$
因为有了黑点不能走的限制,我们发现直接求不好求,所以考虑容斥
对于任意两种方案,显然如果它们路径上经过的第一个黑点是不同的,那么他们一定是不同的
我们现将黑点坐标排序,设$dp_i$表示从$(1,1)$不经过其他黑点到达$i$号黑点的方案数
那么dp方程转移即为:
$dp_i$=$C(x_i+y_i-2,x_i-1)$-$dp_j*C(x_i+y_i-x_j-y_j,x_i-x_j)(x_j \le x_i,y_j \le yi)$
为了方便计算,把$(n,m)$也当做一个黑点即可
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define M 200010 5 #define mod 1000000007 6 #define int long long 7 using namespace std; 8 int n,m,q; 9 int h[M],inv[M],dp[M]; 10 struct black{ 11 int x,y; 12 }a[M]; 13 bool cmp(black a1,black a2){ 14 return a1.x<a2.x||(a1.x==a2.x&&a1.y<a2.y); 15 } 16 int C(int n,int m){ 17 return h[n]*inv[m]%mod*inv[n-m]%mod; 18 } 19 int power(int a,int b){ 20 int ans=1; 21 while(b){ 22 if(b&1) ans=ans*a%mod; 23 b>>=1; a=a*a%mod; 24 } 25 return ans; 26 } 27 void pre(){ 28 h[0]=1;inv[0]=1; 29 for(int i=1;i<=200000;i++) h[i]=i*h[i-1]%mod,inv[i]=power(h[i],mod-2); 30 } 31 #undef int 32 int main(){ 33 #define int long long 34 pre(),cin>>n>>m>>q; 35 for(int i=1;i<=q;i++) cin>>a[i].x>>a[i].y; 36 a[++q]=(black){n,m},sort(a+1,a+1+q,cmp); 37 for(int i=1;i<=q;i++){ 38 dp[i]=C(a[i].x+a[i].y-2,a[i].x-1); 39 for(int j=1;j<i;j++) 40 if(a[j].x<=a[i].x&&a[j].y<=a[i].y) 41 dp[i]=(dp[i]-C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)*dp[j]%mod+mod)%mod; 42 } 43 cout<<dp[q]<<endl; 44 return 0; 45 }
PS:最近不知道为啥突然就变码风了,第一次大括号不换号QwQ