题解:P14468 [COCI 2025/2026 #1] 和谐 / Harmonija
__Creeper__ · · 题解
思路
考虑到路径上每次选择红蓝颜色时,红蓝节点的数量差受到强限制
设当前路径前缀中红点数量为
对于节点
- 染成红色:
x 增加1 ,x - y 增加1 ,状态从j 转移到j + 1 ,获得权值c_u 。 - 染成蓝色:
y 增加1 ,x - y 减少1 ,状态从j 转移到j - 1 ,获得权值p_u 。
在
定义状态向量为
由于矩阵乘法不满足交换律,自底向上与自顶向下的转移顺序不同,需要分别维护:
处理询问
- 若
l = u ,路径完全自顶向下,直接用g 矩阵倍增合并。 - 若
l = v ,路径完全自底向上,直接用f 矩阵倍增合并。 - 若
l 为中途节点,拆分为u \to l (自底向上)和l 的子节点\to v (自顶向下)两段分别计算矩阵并依次相乘。
与初始矩阵相乘后,取所有合法终态的最大值即为答案。
时间复杂度为
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace Creeper{
const int N = 1e5 + 5, inf = -1e18;
int n, q, c[N], p[N], fa[20][N], dep[N];
vector<int> G[N];
struct mtx {
int a[5][5];
void clear() {
for (int i = 0; i <= 4; i++) for (int j = 0; j < 5; j++) a[i][j] = inf;
}
void print() {
for (int i = 0; i <= 4; i++) {
for (int j = 0; j <= 4; j++) {
cout << a[i][j] << ' ';
}
cout << '\n';
}
cout << '\n';
}
}w[N], f[18][N], g[18][N];
mtx operator*(const mtx a, const mtx b) {
mtx res;
res.clear();
for (int i = 0; i <= 4; i++) {
for (int j = 0; j <= 4; j++) {
for (int k = 0; k <= 4; k++) {
res.a[i][j] = max(res.a[i][j], a.a[i][k] + b.a[k][j]);
}
}
}
return res;
}
void dfs(int u, int pre) {
fa[0][u] = pre;
dep[u] = dep[pre] + 1;
f[0][u] = w[pre];
g[0][u] = w[pre];
for (auto v: G[u]) {
if (v == pre) continue;
dfs(v, u);
}
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = 17; i >= 0; i--) {
if (dep[fa[i][u]] >= dep[v]) {
u = fa[i][u];
}
}
if (u == v) return u;
for (int i = 17; i >= 0; i--) {
if (fa[i][u] != fa[i][v]) {
u = fa[i][u];
v = fa[i][v];
}
}
return fa[0][u];
}
mtx kth(int u, int k, int o) {
mtx res;
res.clear();
if (o == 0) {
res = w[u];
for (int i = 17; i >= 0; i--) {
if ((k >> i) & 1) {
res = res * f[i][u];
u = fa[i][u];
}
}
return res;
} else {
res = w[u];
for (int i = 17; i >= 0; i--) {
if ((k >> i) & 1) {
res = g[i][u] * res;
u = fa[i][u];
}
}
return res;
}
}
void solve() {
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> c[i];
for (int i = 1; i <= n; i++) cin >> p[i];
w[0].clear();
for (int i = 1; i <= n; i++) {
w[i].clear();
for (int j = 0; j <= 3; j++) w[i].a[j + 1][j] = c[i];
for (int j = 0; j <= 3; j++) w[i].a[j][j + 1] = p[i];
}
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1, 0);
for (int i = 1; i <= 17; i++) {
for (int j = 1; j <= n; j++) {
fa[i][j] = fa[i - 1][fa[i - 1][j]];
f[i][j] = f[i - 1][j] * f[i - 1][fa[i - 1][j]];
g[i][j] = g[i - 1][fa[i - 1][j]] * g[i - 1][j];
}
}
while (q--) {
int u, v;
cin >> u >> v;
int l = lca(u, v);
mtx ans;
ans.clear();
ans.a[1][2] = 0;
if (l == u) {
mtx x = kth(v, dep[v] - dep[l], 1);
ans = ans * x;
} else if (l == v) {
mtx x = kth(u, dep[u] - dep[l], 0);
ans = ans * x;
} else {
mtx x = kth(u, dep[u] - dep[l], 0);
mtx y = kth(v, dep[v] - dep[l] - 1, 1);
ans = ans * x;
ans = ans * y;
}
cout << max({ans.a[1][0], ans.a[1][1], ans.a[1][2], ans.a[1][3], ans.a[1][4]}) << '\n';
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) Creeper::solve();
}