1. 程式人生 > >第一週周訓 1-1 E大大走格子

第一週周訓 1-1 E大大走格子

大大走格子

有一個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;
}