题意
sol
若某一方胜利,则设该方战胜的区间为 \([l_i,r_i]\),那么过程可描述为 \(1\) 打败 \([l_1,r_1]\),\(2\) 打败 \(l_2,r_2\)…………\(k\) 打败 \([l_k,r_k]\)。显然,\(k\) 打败 \([l_k,r_k]\)…………\(2\) 打败 \(l_2,r_2\),\(1\) 打败 \([l_1,r_1]\) 与之等价,即顺序的正反对结果没有影响。
因此可以将序列翻转进行计算。设 \(f_{u,j}\) 表示从第 \(u\) 个点开始,会被上方的哪一个点击败,特别地,若全部战胜则 \(f_{u,j}=0\)。那么每次操作可以将序列倒序并进行处理,若最终结果为 \(0\) 则胜利,否则失败。
\[f_{u,j}=\left\{\begin{matrix} f_{father,j}(win_{j,c[u]}=true) \\ u(win_{j,c[u]}=false)
\end{matrix}\right. \]
代码
#include <iostream>
#include <algorithm>
#include <cstring>using namespace std;const int N = 200005, M = N * 2, K = 25;int h[N], e[M], ne[M], idx;
int vs[K][K];
int n, m, q;
int c[N], f[N][K];
int w[N];void add(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}void dfs_init(int u, int father){for (int i = 1; i <= m; i ++ ) if (vs[i][c[u]]) f[u][i] = f[father][i];else f[u][i] = u;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (j == father) continue;dfs_init(j, u);}
}int main(){// freopen("ex_tree.in", "r", stdin);memset(h, -1, sizeof h);scanf("%d%d%d", &n, &m, &q);for (int i = 1; i <= m; i ++ )for (int j = 1; j <= m; j ++ )scanf("%d", &vs[i][j]);for (int i = 1; i <= n; i ++ ) scanf("%d", &c[i]);for (int i = 1; i < n; i ++ ){int x, y;scanf("%d%d", &x, &y);add(x, y), add(y, x);}dfs_init(1, 0);while (q -- ){int k, p;scanf("%d%d", &k, &p);for (int i = 1; i <= k; i ++ ) scanf("%d", &w[i]);for (int i = k; i; i -- ) p = f[p][w[i]];if (p) puts("0");else puts("1");}
}