Codeforces Round #774 (Div. 2)D. Weight the Tree
题目大意
一颗 \(n(2\leq n\leq 2\times 10^5)\) 的树,需要为每个点赋予一个权值 \(w_{i}(1\leq w_{i}\leq10^9)\) 。一个节点称为好节点当且仅当其相邻的所有节点的权值和等于该节点的权值,给出一种赋值方案,使得树中好节点的数目最多,并且所有节点的总权值和最小。
思路
考虑树形 \(dp\) ,我们设 \(f[i,1/0]\) 为在以 \(i\) 为根的子树中,是/否选择了节点 \(i\) 作为好节点的情况下,最多的好节点数目以及满足该数目所需的最小权值。显然对于每一个好节点,令其权值为其度数,非好节点的权值为 \(1\) 显然会是最佳方案。并且除了仅有两个节点的情形(可以直接特判),所有的好节点都一定不相邻,于是我们有:
\[f[i,1]=\sum_{j\in son[i]}f[j,0]+(1,deg(i))
\]
\[f[i,0]=\sum_{j\in son[i]}max(f[j,0],f[j,1])+(0,1)
\]
这里的 \(max\) 我们定义为先比较好节点数,再比较权值总和,求和即为好节点数,权值和对应相加。我们在求出答案后,可以通过一遍 \(dfs\) 再根据根据转移的情况来逆推(对于等价的转移情况任选一种即可)求出方案,复杂度 \(O(n)\) 。
代码
#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
#define all(x) x.begin(),x.end()
//#define int LL
//#define lc p*2+1
//#define rc p*2+2
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-8;
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 200010;
struct node {
LL a, b;
void operator+=(const node& rhs) { *this = node{ a + rhs.a,b + rhs.b }; }
bool operator<(const node& rhs)
{
if (a == rhs.a)
return b > rhs.b;
return a < rhs.a;
}
};
LL N;
vector<LL>G[maxn];
node dp[maxn][2];
LL ans[maxn];
void add_edge(LL from, LL to)
{
G[from].push_back(to);
G[to].push_back(from);
}
void DP(LL v, LL p)
{
dp[v][1] = node{ 1,(LL)G[v].size() }, dp[v][0] = node{ 0,1 };
for (int i = 0; i < G[v].size(); i++)
{
LL to = G[v][i];
if (to == p)
continue;
DP(to, v);
if (dp[to][1] < dp[to][0])
dp[v][0] += dp[to][0];
else
dp[v][0] += dp[to][1];
dp[v][1] += dp[to][0];
}
}
void dfs(int v, int p, int t)
{
ans[v] = (t ? (LL)G[v].size() : 1);
for (int i = 0; i < G[v].size(); i++)
{
int to = G[v][i];
if (to == p)
continue;
if (t == 1)
dfs(to, v, 0);
else
{
if (dp[to][0] < dp[to][1])
dfs(to, v, 1);
else
dfs(to, v, 0);
}
}
}
void solve()
{
if (N == 2)
{
cout << 2 << ' ' << 2 << endl;
cout << 1 << ' ' << 1 << endl;
return;
}
DP(1, 0);
if (dp[1][0] < dp[1][1])
{
cout << dp[1][1].a << ' ' << dp[1][1].b << endl;
dfs(1, 0, 1);
}
else
{
cout << dp[1][0].a << ' ' << dp[1][0].b << endl;
dfs(1, 0, 0);
}
for (int i = 1; i <= N; i++)
cout << ans[i] << ' ';
cout << endl;
}
int main()
{
IOS;
cin >> N;
int u, v;
for (int i = 1; i < N; i++)
{
cin >> u >> v;
add_edge(u, v);
}
solve();
return 0;
}