【思路要点】
- 求出 S S S 到 T T T 的路径长度 m m m 以及路径上的每一个点 p a t h i path_i pathi 。
- 记路径上 S → T S\rightarrow T S→T 的方向为向前, T → S T\rightarrow S T→S 的方向为向后。
- 注意到若在 a a a 处的障碍移动到了 b b b 处, b b b 处的障碍移动到了 c c c 处,那么我们也可以认为是 a a a 处的障碍穿过了 b b b 处移动到了 c c c 处,因此,我们可以认为障碍可以穿过障碍移动,但不能停留在障碍上。
- 不难发现,有意义的障碍移动分为两种:
( 1 ) (1) (1) 、从路径上某处 p a t h f r o m path_{from} pathfrom 先向前或向后移动至路径上某个点 p a t h x path_x pathx ,再移入其的子树中,停留在节点 y y y 上。
( 2 ) (2) (2) 、从路径上某处 p a t h f r o m path_{from} pathfrom 先向前移动至路径上某个点 p a t h x path_x pathx ,等棋子拐入 p a t h y ( y < x ) path_y(y<x) pathy(y<x) 的子树后向后移动至路径上某个点 p a t h z ( z < y ) path_z(z<y) pathz(z<y) 。- 需要说明的是, ( 2 ) (2) (2) 中描述的障碍不会移出 S S S 到 T T T 的路径,否则我们可以直接将其视为 ( 1 ) (1) (1) 中描述的障碍。 ( 1 ) (1) (1) 中描述的障碍可以在放下棋子之前完成移动。
- ( 1 ) (1) (1) 中描述的障碍移动距离为 ∣ x − f r o m ∣ + d i s t ( y , p a t h x ) |x-from|+dist(y,path_x) ∣x−from∣+dist(y,pathx) ,
( 2 ) (2) (2) 中描述的障碍移动距离为 2 x − f r o m − z 2x-from-z 2x−from−z 。- 将 ( 1 ) (1) (1) 中描述的障碍移动细分为先向前移动的,称为 ( 1 ) + (1)+ (1)+ ,和先向后移动的,称为 ( 1 ) − (1)- (1)− ,这样可以展开表达式中的绝对值。
- 注意到 ( 1 ) + (1)+ (1)+ 和 ( 2 ) (2) (2) 的移动距离中均含有 − f r o m -from −from 一项,考虑将它们一并处理,因此我们需要在状态中记录尚未匹配的 f r o m from from 端的个数。同时,由于 ( 2 ) (2) (2) 类障碍还存在一端 z z z ,并且这样的 z z z 端必须在经过 y y y ,即棋子拐入的节点后才可以匹配 f r o m from from 端,我们还需要记录尚未匹配的,尚未经过 y y y 的 z z z 端的个数,以及已经经过 y y y 的 z z z 端的个数。
- 接下来我们来设计 d p dp dp ,记状态 ( t , i , j , k ) (t,i,j,k) (t,i,j,k) 表示完成了路径上前 t t t 个点的决策,有 i i i 个尚未匹配的 f r o m from from 端, j j j 个尚未匹配的 z z z 端尚未经过 y y y , k k k 个尚未匹配的 z z z 端已经经过了 y y y 。这里的 i i i 可以是负数,表示需要之后的 − i -i −i 个 f r o m from from 端来走入之前的子树。
- 转移时首先我们需要枚举有多少 f r o m from from 端将走入 t t t 的子树,显然我们会优先选取取深度最小的目标节点。
- 枚举 o u t out out ,即棋子是否在 p a t h t path_t patht 处拐入子树,若拐入,需满足 t t t 处存在子树,步数将增加 2 2 2 ,并且所有尚未经过 y y y 的 z z z 端在此之后会转化为已经经过 y y y 的 z z z 端。
- 枚举 l f t lft lft ,即是否用 p a t h t path_t patht 处的障碍匹配一个已经经过 y y y 的 z z z 端,若是,需满足 p a t h t path_t patht 处存在障碍,并且存在可以匹配的 z z z 端,步数将增加 t t t ,可以匹配的 z z z 端数量将减少 1 1 1 。
- 枚举 r e v rev rev ,即是否将 t t t 处作为 ( 2 ) (2) (2) 类障碍的 x x x 点匹配一对 f r o m , z from,z from,z 端,若是,需满足 l f t = f a l s e lft=false lft=false ,并且存在可以匹配的 z z z 端,步数将增加 2 t 2t 2t , f r o m from from 端和可以匹配的 z z z 端数量将减少 1 1 1 。
- 枚举 e x t ext ext ,即是否将 t t t 处的空位用作一个新的 ( 2 ) (2) (2) 类障碍的 z z z 端,若是,步数将增加 − t -t −t ,尚未经过 y y y 的 z z z 端将增加 1 1 1 。
- 若 p a t h t path_t patht 处的障碍没有用作匹配已经经过 y y y 的 z z z 端,则需要将其计入 f r o m from from 端的个数中。
- 所有 p a t h t path_t patht 的子树大小和为 O ( N ) O(N) O(N) ,因此时间复杂度为 O ( N M 3 ) O(NM^3) O(NM3) ,需要滚动数组优化空间。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 305; const int MAXM = 105; const int INF = 1e9; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } char mp[MAXN]; int s, t; vector <int> a[MAXN]; int cnt, rnk[MAXN], path[MAXN]; int timer, dfn[MAXN], pos[MAXN]; int n, belong[MAXN], dist[MAXN]; int dp[2][MAXM * 2][MAXM][MAXM]; bool online[MAXN], outside[MAXN]; bool dfs(int pos, int fa) { bool ans = pos == s; for (auto x : a[pos]) if (x != fa) ans |= dfs(x, pos); if (ans) { online[pos] = true; rnk[pos] = ++cnt; path[cnt] = pos; } return ans; } void work(int cur, int fa, int home, int dis) { dist[cur] = dis; belong[cur] = home; if (dis >= 2) { dfn[cur] = ++timer; pos[timer] = cur; } for (auto x : a[cur]) if (x != fa) work(x, cur, home, dis + 1); } int main() { read(n), read(s), read(t); scanf("\n%s", mp + 1); for (int i = 1; i <= n - 1; i++) { int x, y; read(x), read(y); a[x].push_back(y); a[y].push_back(x); } assert(dfs(t, 0)); for (int i = 1; i <= cnt; i++) { dfn[path[i]] = ++timer; pos[timer] = path[i]; for (auto x : a[path[i]]) if (!online[x]) { outside[path[i]] = true; work(x, path[i], path[i], 1); } } for (int i = 0; i <= cnt * 2; i++) for (int j = 0; j <= cnt; j++) for (int k = 0; j + k <= cnt; k++) dp[1][i][j][k] = INF; dp[1][cnt][0][0] = 0; for (int t = 1, now = 1, dest = 0; t <= timer; t++, swap(now, dest)) { for (int i = 0; i <= cnt * 2; i++) for (int j = 0; j <= cnt; j++) for (int k = 0; j + k <= cnt; k++) dp[dest][i][j][k] = INF; for (int i = 0; i <= cnt * 2; i++) for (int j = 0; j <= cnt; j++) for (int k = 0; j + k <= cnt; k++) { // i : carry, j : right, not revsered, k : right, reverseed. int tmp = dp[now][i][j][k]; if (tmp >= n * n * n + 2333) continue; int now = pos[t]; if (!online[now]) { chkmin(dp[dest][i][j][k], tmp); auto sign = [&] (int x) {return (x <= cnt) ? -1 : 1; }; if (i >= 1) chkmin(dp[dest][i - 1][j][k], tmp + dist[now] + sign(i) * rnk[belong[now]]); continue; } for (int out = 0; out <= outside[now]; out++) for (int lft = 0; lft <= (mp[now] == 'X') && lft <= k; lft++) for (int rev = 0; rev + lft <= 1 && rev <= k && rev <= i; rev++) for (int ext = 0; ext <= 1; ext++) { int steps = tmp, di = i, dj = j, dk = k; if (out) dk += dj, dj = 0, steps += 2; if (lft) dk--, steps += rnk[now]; if (rev) dk--, di--, steps += rnk[now] * 2; if (ext) dj++, steps -= rnk[now]; auto sign = [&] (int x) {return (x < cnt) ? 1 : -1; }; if ((mp[now] == 'X') - lft) chkmin(dp[dest][di + 1][dj][dk], steps + sign(di) * rnk[now]); else chkmin(dp[dest][di][dj][dk], steps); } } } int ans = cnt + dp[(timer + 1) & 1][cnt][0][0]; if (ans >= n * n * n + 2333) writeln(-1); else writeln(ans); return 0; }