题意
有一个人站在二维平面的原点处。
他将会进行\(N\)次传送,每次传送他可以做如下三种移动中的一种:
- 从当前位置\((X,Y)\)移动到\((X+A,Y+B)\)
- 从当前位置\((X,Y)\)移动到\((X+C,Y+D)\)
- 从当前位置\((X,Y)\)移动到\((X+E,Y+F)\)
有\(M\)个障碍物,分别位于\((X_1,Y_1),\dots, (X_M, Y_M)\),他不能传送到这些点上。
问经过\(N\)次传送,会产生多少条路径。
题目链接:https://atcoder.jp/contests/abc265/tasks/abc265_e
数据范围
\(1 \leq N \leq 300\)
\(0 \leq M \leq 10^5\)
\(-10^9 \leq A,B,C,D,E,F \leq 10^9\)
思路
这里提供两种思路。两种思路都是使用DP。
第一种思路,令\(f(n, x, y)\)表示\(n\)次传送,终点是\((x,y)\)的路径条数,转移方程显然。但是坐标范围太大,真的可以这样做吗?其实能到达的点的数量并不多。我们可以分析一下,三种传送方式的个数决定了终点,并且在给定总次数的情况下,固定前两种传送方式的个数,第三种传送方式的个数也固定了。因此,传送到的点数其实只有\(O(N^2)\)。在实际编写代码的过程中,不能直接使用数组进行转移,可以使用map来做。
第二种思路,令\(f(n,x,y)\)表示\(n\)次传送,其中第一种传送使用次数为\(x\),第二种传送使用次数为\(y\),可以产生多少条路径。转移方程显然。
代码
思路1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int N = 310, M = 100010, mod = 998244353;
int n, m;
ll dx[5], dy[5];
map<pii, ll> f;
set<pii> st;
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0; i < 3; i ++) {
scanf("%lld%lld", &dx[i], &dy[i]);
}
for(int i = 1; i <= m; i ++) {
ll x, y;
scanf("%lld%lld", &x, &y);
st.insert({x, y});
}
f[{0, 0}] = 1;
for(int i = 0; i < n; i ++) {
map<pii, ll> g;
for(auto p : f) {
ll x = p.first.first, y = p.first.second, val = p.second;
for(int j = 0; j < 3; j ++) {
ll tx = x + dx[j], ty = y + dy[j];
if(st.find({tx, ty}) != st.end()) continue;
g[{tx, ty}] = (g[{tx, ty}] + val) % mod;
}
}
swap(f, g);
}
ll res = 0;
for(auto p : f) {
res = (res + p.second) % mod;
}
printf("%lld\n", res);
return 0;
}
思路2
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int N = 310, M = 100010, mod = 998244353;
int n, m;
ll dx[5], dy[5];
ll f[N][N][N];
map<pii, bool> st;
int main()
{
scanf("%d%d", &n, &m);
for(int i = 0; i < 3; i ++) {
scanf("%lld%lld", &dx[i], &dy[i]);
}
for(int i = 1; i <= m; i ++) {
ll x, y;
scanf("%lld%lld", &x, &y);
st[{x, y}] = true;
}
f[0][0][0] = 1;
for(ll i = 1; i <= n; i ++) {
for(ll j = 0; j <= i; j ++) {
for(ll k = 0; k <= i; k ++) {
ll z = i - j - k;
if(z < 0) continue;
ll X = j * dx[0] + k * dx[1] + z * dx[2];
ll Y = j * dy[0] + k * dy[1] + z * dy[2];
if(st.count({X, Y})) {
f[i][j][k] = 0;
continue;
}
f[i][j][k] = (f[i - 1][j][k] + f[i - 1][j - 1][k]) % mod;
f[i][j][k] = (f[i][j][k] + f[i - 1][j][k - 1]) % mod;
}
}
}
ll res = 0;
for(int i = 0; i <= n; i ++) {
for(int j = 0; j <= n; j ++) {
if(i + j > n) continue;
res = (res + f[n][i][j]) % mod;
}
}
printf("%lld\n", res);
return 0;
}