ABC265 E - Warp
DP
https://atcoder.jp/contests/abc265/tasks/abc265_e
题意
有个人初始在原点,有三个向量 \(v[0],v[1],v[2]\), 每次操作可以选择任意一个向量,若当前在 \(p\) 点,可以瞬移到 \(p+v\),一共有 n 次操作(n<=300)
有 m 个陷阱(m<=1e5),陷阱的地方不能到达,求这个人完成这 n 次操作的路径的方案数
思路
由于 n 很小,可以设 \(f[w][i][j]\) 表示已经走了 w 次,k = w - i - j, \((i,j,k)\) 表示三个向量分别走了 i, j, k 次
\(f[w][i][j]\) 为路径的方案数
陷阱用 set 存起来判断即可,注意到是瞬移,所以只需要在某次操作的终点判断是不是在陷阱上,不需要在这次操作对应的线段上判断
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
using namespace std;
#define endl "\n"
typedef long long ll;
const int N = 310;
const int mod = 998244353;
int n, m;
struct Point
{
ll x, y;
Point operator+(const Point &t) const
{
return {x + t.x, y + t.y};
}
Point operator*(const ll &k) const
{
return {x * k, y * k};
}
bool operator<(const Point &t) const
{
if (x == t.x)
return y < t.y;
return x < t.x;
}
};
using Vector = Point;
set<Point> st;
Vector v[3];
ll f[N][N][N];
void add(ll &a, ll b)
{
a += b;
if (a >= mod)
a -= mod;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 0; i < 3; i++)
cin >> v[i].x >> v[i].y;
while(m--)
{
ll x, y;
cin >> x >> y;
st.insert((Point){x, y});
}
f[0][0][0] = 1;
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= i; j++)
{
for (int k = 0; k + j <= i; k++)
{
int w = i - j - k;
Point ed;
ed = v[0] * (j + 1) + v[1] * k + v[2] * w;
if (!st.count(ed))
add(f[i+1][j+1][k], f[i][j][k]);
ed = v[0] * j + v[1] * (k + 1) + v[2] * w;
if (!st.count(ed))
add(f[i+1][j][k+1], f[i][j][k]);
ed = v[0] * j + v[1] * k + v[2] * (w + 1);
if (!st.count(ed))
add(f[i+1][j][k], f[i][j][k]);
}
}
}
ll ans = 0;
for (int j = 0; j <= n; j++)
for (int k = 0; k + j <= n; k++)
add(ans, f[n][j][k]);
cout << ans << endl;
return 0;
}