SPS的跳马【dp+组合数】
【题目大意】
sps有左手和右手,他的左手能让他的马的坐标从$(x, y)$变成$(x+ax, y+ay)$,他的右手能让他的马的坐标从$(x, y)$变成$(x+bx, y+by)$,他的马初始在$(0, 0)$,有$n$个奇怪的格子不能通行。求他的马到$(endx, endy)$的方法数,模$10^9+7$。给定$ax, ay, bx, by, endx, endy, n$以及不能通过的格子的坐标。
$ n \leq 500, -500 \leq ax, bx, ay, by, endx, endy \leq 500, ay \times bx \not= ax \times by$
【题解】
首先把左右手看做基,重新坐标化,题目就变成了,向右走一格或向下走一格。
首先$(a, b)$到$(c, d)$的方案数为$C(c-a+d-b, c-a)$。
然后设$f_i$表示$i$到终点不经过任何障碍的方案数,$w_i$表示$i$到终点没有限制的方案数,$x_{i,j}$表示障碍$i$到障碍$j$的方案数。$f_i = w_i - \sum x_{i,j} \times f_j$
随便转移转移啦!
# include <stdio.h>
# include <algorithm>
# include <iostream>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
int n, endx, endy, ax, ay, bx, by, x[510], y[510], ex, ey;
ll X[510][510], f[510], inv[1000010], fac[1000010];
int nx[510], ny[510];
inline bool relabel(int &x, int &y, int a, int b) {
int fz1 = a*by-b*bx, fm1 = ax*by-ay*bx, fz2 = a*ay-b*ax, fm2 = bx*ay-ax*by;
if(fz1 % fm1 != 0 || fz2 % fm2 != 0) return false;
x = fz1/fm1, y = fz2/fm2;
if(x < 0 || y < 0) return false;
return true;
}
inline ll power(ll a, int b) {
ll s=1;
while(b) {
if(b&1) s=s*a%mod;
a=a*a%mod;
b>>=1;
}
return s;
}
inline ll C(int a, int b) {
return fac[a] * inv[b] % mod * inv[a-b] % mod;
}
// (a, b) -> (c, d)
inline ll getr(int a, int b, int c, int d) {
if(c<a || d<b) return 0ll;
return C(c+d-a-b, c-a);
}
int main() {
freopen("hands.in", "r", stdin);
freopen("hands.out", "w", stdout);
fac[0] = inv[0] = 1;
for (int i=1; i<=1000000; ++i) {
fac[i] = fac[i-1] * i % mod;
inv[i] = power(fac[i], mod-2);
}
scanf("%d%d%d", &endx, &endy, &n);
scanf("%d%d%d%d", &ax, &ay, &bx, &by);
for (int i=1; i<=n; ++i)
scanf("%d%d", &nx[i], &ny[i]);
if(relabel(ex, ey, endx, endy) == 0) {
puts("0");
return 0;
}
//printf("ok 1\n");
int tn=0;
for (int i=1; i<=n; ++i) {
int newx, newy;
if(relabel(newx, newy, nx[i], ny[i])) {
if(newx > ex || newy > ey) continue;
++tn;
x[tn]=newx, y[tn]=newy;
}
}
//printf("ok 2\n");
n = tn;
for (int i=1; i<=n; ++i)
for (int j=i+1; j<=n; ++j)
if(x[i] > x[j] || (x[i] == x[j] && y[i] > y[j])) {
swap(x[i], x[j]);
swap(y[i], y[j]);
}
//printf("ok 3\n");
/*
printf("============================\n");
for (int i=1; i<=n; ++i)
printf("newx = %d, newy = %d\n", x[i], y[i]);
printf("============================\n");
*/
for (int i=1; i<=n; ++i) {
f[i] = getr(x[i], y[i], ex, ey);
for (int j=i+1; j<=n; ++j)
X[i][j] = getr(x[i], y[i], x[j], y[j]);
}
//printf("ok 4\n");
for (int i=n; i>=1; --i) {
for (int j=i+1; j<=n; ++j) {
f[i] = (f[i] - X[i][j] * f[j] % mod);
if(f[i] < 0) f[i] += mod;
}
}
//printf("ok 5\n");
//printf("ex = %d, ey = %d\n", ex, ey);
ll ans = getr(0, 0, ex, ey);
//printf("ok 6\n");
//cout << "ans = " << ans << endl;
for (int i=1; i<=n; ++i) {
ll tmp = getr(0, 0, x[i], y[i]);
// cout << tmp << ' ' << f[i] << endl;
tmp = tmp * f[i] % mod;
ans = ans - tmp;
if(ans < 0) ans += mod;
}
//printf("ok 7\n");
if(ans < 0) ans += mod;
cout << ans << endl;
return 0;
}
/*
4 4 1
0 1 1 0
2 3
70
*/
2023年4月21日 16:16
Our goal is to meet the needs of people of all ages by publishing news classed as General, sample-paper.com, sample-paper.comntertainment, education, and world news are all covered.Our reporting team plans to provide the Education & Recruitment Update for all age groups and to present the actual picture of recent events through inside coverage. and so on. Political,