P5765题解
感觉 P5765 和 P4395 都没有对于最大点权太认真的证明,故写了此题解。第一次自己证明这种结论如有纰漏或错误欢迎指出。
题目描述
题目给出一个树的边,要求自选正整数点权值(即填色),使得点权和最小,同时要求相邻的两个点的点权不等。
Solution
树形动规的绿题一般比较相似,这个题就和上司的舞会很像。
动规方程也很好推,令
则
考虑用
讨论区也有说最大用
考虑正解,对于本题任意一颗树的填色方案,总有一个最大的点权。那么 dp 的前置问题转化为:一个
我比较菜直接想不出来,但是可以把这个问题转化:已经填好树上点权且最优,若这个点的点权为
若最优答案树上一个点的点权为
发现最大点权
code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<deque>
#include<map>
using namespace std;
inline int red() {
int op = 1, x = 0;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') op = -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
}
return op * x;
}
const int maxn = 1e5 + 10, maxm = 1e5 + 10, maxx = 16;
const int inf = 0x3f3f3f3f;
int n, x;
int root;
int deg[maxn];
int f[maxn][maxx];
int head[maxn], to[maxm], nxt[maxm];
int num;
void add(int a, int b) {
to[++num] = b;
nxt[num] = head[a];
head[a] = num;
}
void dfs(int u, int fa) {
for(int i = 1; i <= x; ++i) {
f[u][i] = i;
}
for(int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if(v == fa) continue;
dfs(v, u);
for(int j = 1; j <= x; ++j) {
int minn = inf;
for(int k = 1; k <= x; ++k) {
if(k == j) continue;
minn = min(minn, f[v][k]);
}
f[u][j] += minn;
}
}
}
int main() {
n = red();
x = log(n) / log(2);
++x;
for(int i = 1; i < n; ++i) {
int a = red(), b = red();
add(a, b);
add(b, a);
++deg[a], ++deg[b];
}
root = 1;
dfs(root, -1);
int ans = inf;
for(int i = 1; i <= x; ++i) {
ans = min(ans, f[1][i]);
}
printf("%d\n", ans);
return 0;
}