题面
题意
给出一棵树,每条边有初始状态。对一条路径上的边的状态进行翻转,求每条边满足结果且操作数最少,最小的操作边数之和。
思路
设f[i][0/1]表示i与fa[i]的边是否需要翻转时的答案。
枚举每个儿子,判断儿子与i的边是否处于同一个操作次数(等方案)进行转移。
代码
#include <vector>
#include <cstdio>
#include <stdlib.h>
#include <algorithm>
const std::pair<int, int> inf = std::make_pair(1e9, 1e9);
std::vector<std::pair<int, int> > g[200001];
std::pair<int, int> f[100001][2];
int n;
std::pair<int, int> operator+ (std::pair<int, int> a, std::pair<int, int> b) {
return std::make_pair(a.first + b.first, a.second + b.second);
}
void dfs(int fa, int p, int type) {
std::pair<int, int> tmp0(0, 0), tmp1(inf);
for (int i = 0; i < g[p].size(); i++) {
int v = g[p][i].first;
if (v == fa) continue;
dfs(p, v, g[p][i].second);
std::pair<int, int> nxt0, nxt1;
nxt0 = std::min(tmp0 + f[v][0], tmp1 + f[v][1]);
nxt1 = std::min(tmp0 + f[v][1], tmp1 + f[v][0]);
tmp0 = nxt0;
tmp1 = nxt1;
}
if (!type || type == 2)
f[p][0] = std::min(tmp0, std::make_pair(tmp1.first + 1, tmp1.second));
else f[p][0] = inf;
if (type == 1 || type == 2)
f[p][1] = std::min(std::make_pair(tmp0.first + 1, tmp0.second + 1), std::make_pair(tmp1.first, tmp1.second + 1));
else f[p][1] = inf;
}
int main() {
int size = 256 << 20;
char *p = (char*)malloc(size) + size;
__asm__("movl %0, %%esp\n" :: "r"(p));
scanf("%d", &n);
for (int i = 1, a, b, c, d; i < n; i++) {
scanf("%d %d %d %d", &a, &b, &c, &d);
if (d != 2) d = c ^ d;
g[a].push_back(std::make_pair(b, d));
g[b].push_back(std::make_pair(a, d));
}
dfs(0, 1, 0);
printf("%d %d", f[1][0].first / 2, f[1][0].second);
}