BZOJ4767: 兩雙手【組合數學+容斥原理】

Description

老W是個棋藝高超的棋手,他最喜歡的棋子是馬,更具體地,他更加喜歡馬所行走的方式。老W下棋時以爲無聊,便決定增強馬所行走的方式,更具體地,他有兩雙手,其中一雙手能讓馬從(u,v)移動到(u+Ax,v+Ay)而另外一雙手能讓馬從(u,v)移動到(u+Bx,v+By)。小W看見老W的下棋方式,以爲很是有趣,他開始思考一個問題:假設棋盤是個無限大的二維平面,一開始馬在原點(0,0)上,若用老W的兩種方式進行移動,他有多少種不一樣的移動方法到達點(Ex,Ey)呢?兩種移動方法不一樣當且僅當移動步數不一樣或某一步所到達的點不一樣。老W聽了這個問題,以爲還不夠有趣,他在平面上又設立了n個禁止點,表示馬不能走到這些點上,如今他們想知道,這種狀況下馬有多少種不一樣的移動方法呢?答案數可能很大,你只要告訴他們答案模(10^9+7)的值就行。c++

Input

第一行三個整數Ex,Ey,n分別表示馬的目標點座標與禁止點數目。spa

第二行四個整數Ax,Ay,Bx,By分別表示兩種單步移動的方法,保證AxBy-AyBx≠0code

接下來n行每行兩個整數Sxi,Syi,表示一個禁止點。ip

|Ax|,|Ay|,|Bx|,|By| <= 500, 0 <= n,Ex,Ey <= 500input

Output

僅一行一個整數,表示所求的答案。it

Sample Input

4 4 1
0 1 1 0
2 3io

Sample Output

40ast


思路

首先發現能夠算出來從起點到任何一個點的兩次操做的步數class

而後就轉化成了路徑計數問題,而後就能夠套一個容斥方法

\(dp_{i}\)表示從原點到i不通過任何黑點的方案數,這樣就能夠枚舉從原點到如今的第一個通過的黑點所對應的方案數

而後就能夠算了

注意算的時候不能把第一次或者第二次操做次數大於目標點的點算進去,否則會出鍋


#include<bits/stdc++.h>

using namespace std;

typedef pair<int, int> pi;
const int Mod = 1e9 + 7;
const int N = 1e3 + 10;
const int M = 3e6 + 10;

int n, ex, ey, ax, ay, bx, by;
int inv[M], fac[M], dp[M];
pi p[N];

int add(int a, int b) {
  return (a += b) >= Mod ? a - Mod : a;
}

int sub(int a, int b) {
  return (a -= b) < 0 ? a + Mod : a;
}

int mul(int a, int b) {
  return 1ll * a * b % Mod;
}

int fast_pow(int a, int b) {
  int res = 1;
  while (b) {
    if (b & 1) res = mul(res, a);
    b >>= 1;
    a = mul(a, a);
  }
  return res;
}

int C(int a, int b) {
  if (a < 0 || b < 0) return 0;
  return mul(fac[a + b], mul(inv[b], inv[a]));
}

void init() {
  fac[0] = inv[0] = 1;
  for (int i = 1; i < M; i++) fac[i] = mul(fac[i - 1], i);
  inv[M - 1] = fast_pow(fac[M - 1], Mod - 2);
  for (int i = M - 2; i >= 1; i--) inv[i] = mul(inv[i + 1], i + 1);
}

pi calc(int x, int y) {
  int a = -1, b = -1;
  if ((x * by - y * bx) % (ax * by - ay * bx) == 0)
    a = (x * by - y * bx) / (ax * by - ay * bx);
  if ((x * ay - y * ax) % (bx * ay - by * ax) == 0)
    b = (x * ay - y * ax) / (bx * ay - by * ax);
  return pi(a, b);
}

int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  init();
  scanf("%d %d %d %d %d %d %d", &ex, &ey, &n, &ax, &ay, &bx, &by);
  int cnt = 0;
  p[++cnt] = calc(ex, ey);
  if (p[1].first < 0 || p[1].second < 0) {
    puts("0");
    return 0;
  }  
  for (int i = 1; i <= n; i++) {
    int u, v; scanf("%d %d", &u, &v);
    pi cur = calc(u, v);
    if (cur.first < 0 || cur.second < 0 || cur.first > p[1].first || cur.second > p[1].second) continue;
    //須要特判  cur.first > p[1].first || cur.second > p[1].second
    p[++cnt] = cur;
  }
  n = cnt;
  sort(p + 1, p + cnt + 1);
  n = unique(p + 1, p + cnt + 1) - p - 1;
  for (int i = 1; i <= n; i++) {
    dp[i] = C(p[i].first, p[i].second);
    for (int j = 1; j < i; j++) {
      dp[i] = sub(dp[i], mul(dp[j], C(p[i].first - p[j].first, p[i].second - p[j].second)));
    }
  }
  printf("%d", dp[n]);
  return 0;
}
相關文章
相關標籤/搜索