第一週周訓 1-1 E大大走格子
阿新 • • 發佈:2018-12-11
大大走格子
有一個h行w列的棋盤,裡面有一些格子是不能走的,現在要求從左上角走到右下角的方案數。
Input
單組測試資料。
第一行有三個整數h, w, n(1 ≤ h, w ≤ 10^5, 1 ≤ n ≤ 2000),表示棋盤的行和列,還有不能走的格子的數目。
接下來n行描述格子,第i行有兩個整數ri, ci (1 ≤ ri ≤ h, 1 ≤ ci ≤ w),表示格子所在的行和列。
輸入保證起點和終點不會有不能走的格子。
Output
輸出答案對1000000007取餘的結果。
Sample Input
3 4 2
2 2
2 3
Sample Output
2
看到題以為是組合數的題,我們做過機器人走方格。現在有了黑點,考慮補集思想和容斥原理。那麼最終答案=way[任意走]−way[途經至少一個黑點]+way[途經至少兩個黑點]−way[途經至少三個黑點]...way[任意走]−way[途經至少一個黑點]+way[途經至少兩個黑點]−way[途經至少三個黑點]...(其中way表示方案數)。而N=2000顯然是不能列舉集合的,而且目前我所學的知識沒辦法解決,我們就需要本題容斥的特殊性了:不同物件之間存在依賴性與階段性。也就是說我們選出至少兩個黑點是在先選出一個黑點的基礎上,考慮這個黑點還能到達哪些黑點從而得到的,以此類推。
我們所求的答案是走到 點(n,m)不經過任何一個黑點的路徑數量;
我們先對點進行排序 // x升序 ,y升序
用 dp[i]表示從 (1,1)到第i個點不經過任何一個黑點的路徑數量。
所以為(n,m)就是第 k+1 個點。
dp[i]=c(x1+y1-2,x1-1);//初始化
動態轉移方程 dp[i]=(dp[i]-dp[j]*c(x1-x2+y1-y2,x1-x2)%mod+mod)%mod;
#include<bits/stdc++.h> using namespace std; typedef long long ll; const long long mod=1000000007; ll jc[200050]= {1}; struct node { int x; int y; } black[2020]; bool cmp(node a,node b) { if(a.x==b.x) return a.y<b.y; return a.x<b.x; } ll power(ll a,ll b,ll c) { ll ans=1; while(b>0) { if(b&1) ans=ans*a%c; a=a*a%c; b=b>>1; } return ans; } ll c(ll a,ll b) { return jc[a]*power(jc[b]*jc[a-b]%mod,mod-2,mod)%mod; } int main() { for(ll i=1; i<=200000; i++) ///預處理 jc[i]=jc[i-1]*i%mod; ll n,m,k,dp[2020];memset(dp,0,sizeof(dp)); cin>>n>>m>>k; for(int i=1; i<=k; i++) cin>>black[i].x>>black[i].y; black[++k]=node{n,m}; sort(black+1,black+1+k,cmp); for(int i=1; i<=k; i++) { ll x1=black[i].x,y1=black[i].y; dp[i]=c(x1+y1-2,x1-1); for(int j=1; j<i; j++) { ll x2=black[j].x,y2=black[j].y; if(x2<=x1&&y2<=y1)///判斷條件 dp[i]=(dp[i]-dp[j]*c(x1-x2+y1-y2,x1-x2)%mod+mod)%mod; } } cout<<dp[k]<<endl; return 0; }