【BZOJ】4767: 两双手【组合数学】【容斥】【DP】
4767: 两双手
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 1057 Solved: 318
[Submit][Status][Discuss]
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)的值就行。
Input
第一行三个整数Ex,Ey,n分别表示马的目标点坐标与禁止点数目。
第二行四个整数Ax,Ay,Bx,By分别表示两种单步移动的方法,保证Ax*By-Ay*Bx≠0
接下来n行每行两个整数Sxi,Syi,表示一个禁止点。
|Ax|,|Ay|,|Bx|,|By| <= 500, 0 <= n,Ex,Ey <= 500
Output
仅一行一个整数,表示所求的答案。
Sample Input
4 4 1
0 1 1 0
2 3
0 1 1 0
2 3
Sample Output
40
Solution
看起来很像一般的网格图求路径数,在没有障碍物的情况下,处理出到达终点分别用两种步数走的步数(解方程唯一固定,再用组合数学,方案数就等于$C(x+y,x)$,$x$和$y$分别是两种步数的次数。
可是这道题有障碍格。
所以在上述基础上进行容斥DP即可。将能走到的所有障碍点的$x$和$y$(从原点出发)预处理出来,(将终点的$x$、$y$值也放入结构体)排序后,后面的DP值要容斥减去前面能到达它的方案数。最后答案就是$dp[n]$。(因为一开始保证了结构体里所有障碍点都比目标点要小)
【注意】预处理阶乘的逆元线性从后往前线性求得,不然会TLE!
Code
#include<bits/stdc++.h> #define mod 1000000007 #define LL long long using namespace std; int Ax, Ay, Bx, By, n, Ex, Ey; struct Node { int x, y; } QAQ[1005]; bool cmp(Node a, Node b) { if(a.x == b.x) return a.y < b.y; return a.x < b.x; } LL mpow(LL a, LL b) { LL ans = 1; for(; b; b >>= 1, a = a * a % mod) if(b & 1) ans = ans * a % mod; return ans; } void cal(int &x, int &y) {////解方程计算两种步数 LL a1, a2, b1, b2; b1 = y * Ax - x * Ay, b2 = Ax * By - Ay * Bx; a1 = x * By - y * Bx, a2 = Ax * By - Ay * Bx; if(a2 == 0 || b2 == 0) { x = -1, y = -1; return ; } if((a1 / a2) * a2 != a1 || (b1 / b2) * b2 != b1) { x = -1, y = -1; return ; } x = a1 / a2, y = b1 / b2; } LL fac[1000005], inv[1000005]; LL C(LL a, LL b) { if(a < b) return 0; return fac[a] * inv[a-b] % mod * inv[b] % mod; } void init() { fac[0] = 1; for(int i = 1; i <= 1000000; i ++) fac[i] = fac[i-1] * i % mod; inv[0] = 1; inv[1000000] = mpow(fac[1000000], mod - 2); for(int i = 999999; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % mod;////线性求阶乘逆元 } LL f[1005]; int main() { scanf("%d%d%d", &Ex, &Ey, &n); scanf("%d%d%d%d", &Ax, &Ay, &Bx, &By); cal(Ex, Ey); for(int i = 1; i <= n; i ++) { scanf("%d%d", &QAQ[i].x, &QAQ[i].y); cal(QAQ[i].x, QAQ[i].y); if(QAQ[i].x < 0 || QAQ[i].y < 0 || QAQ[i].x > Ex || QAQ[i].y > Ey) {/////不合法的步数筛掉 n --; i --; } } QAQ[++n].x = Ex, QAQ[n].y = Ey; sort(QAQ + 1, QAQ + 1 + n, cmp); init(); for(int i = 1; i <= n; i ++) { f[i] = C(QAQ[i].x + QAQ[i].y, QAQ[i].x); if(f[i] == 0) continue; for(int j = 1; j < i; j ++) { f[i] -= (f[j] * C(QAQ[i].x - QAQ[j].x + QAQ[i].y - QAQ[j].y, QAQ[i].x - QAQ[j].x)) % mod;/////容斥 f[i] = (f[i] % mod + mod) % mod; } } printf("%lld", f[n]); return 0; }