这道题目是比较经典的树上阶梯博弈。
设一个点的深度是\(dep_i\),如果两个点\(i,j\)满足\(dep_i\not\equiv dep_j \mod k\),则两个点对答案的影响是完全独立的。
我们可以把图拆分为\(k\)部分,并且按照原图中的祖先关系把新图连接为\(k\)棵树。对于一个点\(i\),在新图中的深度为\(dep_i'=\left \lfloor \frac{dep_i}{k}\right\rfloor\)。考虑只有当\(dep_i'\)为奇数时才会对答案有影响。对于任意偶数深度的点,如果先手把他移动到偶数深度,后手一定可以通过一步操作把他重新移动到偶数深度。这一步的思考过程就是阶梯博弈。
我们可以通过DFS求出一个点做根的解,但是本题要求的是每一个点做根。对于这类题目考虑换根 DP。
记\(f[i][j][l]\),表示以\(i\)为子树,\(j \equiv dep \mod k, l \equiv \left\lfloor \frac{dep'}{l} \right\rfloor \mod 2\),然后进行换根 DP。换根的时候,只有当\(j=k-1\)时,\(l\)才会发生变化。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using i64 = long long;
#define int i64
using vi = vector<int>;
using pii = pair<int, int>;
const i32 inf = INT_MAX / 2;
const int mod = 1e9 + 7;
vi a;
vector<vi> e;
vector<vector<vi>> g;
int k;
void dfs(int x, int fa) {
g[x][0][0] = a[x];
for (auto y: e[x]) {
if (y == fa)continue;
dfs(y, x);
for (int j = 0; j < k; j++)
for (int l = 0; l < 2; l++) {
int nj = j, nl = l;
nj++;
if (nj == k) nj = 0, nl ^= 1;
g[x][nj][nl] ^= g[y][j][l];
}
}
return;
}
void dp(int x, int fa) {
if (x != 1) {
auto tmp = g[fa];
for (int j = 0; j < k; j++) {
for (int l = 0; l < 2; l++) {
int nj = j + 1, nl = l;
if (nj == k) nj = 0, nl ^= 1;
tmp[nj][nl] ^= g[x][j][l];
}
}
for (int j = 0; j < k; j++)
for (int l = 0; l < 2; l++) {
int nj = j + 1, nl = l;
if (nj == k) nj = 0, nl ^= 1;
g[x][nj][nl] ^= tmp[j][l];
}
}
for (auto y: e[x]) {
if (y == fa) continue;
dp(y, x);
}
return;
}
i32 main() {
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n >> k;
e = vector<vi>(n + 1);
for (int i = 1, x, y; i < n; i++) {
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
a = vi(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
g = vector(n + 1, vector(k, vi(2)));
dfs(1, -1), dp(1, -1);
for (int i = 1, sg; i <= n; i++) {
sg = 0;
for (int j = 0; j < k; j++) sg ^= g[i][j][1];
cout << (sg != 0) << " ";
}
return 0;
}
标签:nj,nl,21,int,++,CodeCraft,Codeforces,dep,vector
From: https://www.cnblogs.com/PHarr/p/18546060