【补题】Codeforces Round 958 (Div. 2) D. The Omnipotent Monster Killer
题意:给出n个点的树,每个点有一个权值,在每个时间点的结束时刻,你可以选择去除掉任意数量的节点,但是要满足这个要求:不能同时删除要删除点的相邻点(由一条边相连的点)
问权值最小是多少
思路:题解:CF1988D The Omnipotent Monster Killer - 洛谷专栏
这篇题解记录了本人思考这道题时候的过程,所以会有些步骤不重要
1.首先这题上来直接想到了分奇偶点然后直接删除,但是会出现权值大的点在一起,分奇偶并不能秒了,但是这为我们后面的一个优化提供了帮助
花费的时间不会超过logn,因为你删除一个节点,为了杀死的更多,你一定会把任何不直接相连的节点一起杀死,拿链做比方1-2-3-4,最坏就是1,4杀死,然后第2轮杀死2,第3轮杀3,就是最坏情况,因此这道题的数据量保证了时间不会超过log3e5,给了对记录时间的机会
2.因为已经可以按照时间分类,且任何一个节点删去的时间点只与相邻节点关联,所以任意一个节点对于它的子树可以记录下最好的状态,且祖先节点不会受影响,满足dp的递归过程,可以树形dp了
dp非常好写:对于一个节点在不同时间段被删的时刻,对于自己的子树选择最优的方案,从而递归回根节点
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS \std::ios::sync_with_stdio(0); \std::cin.tie(0); \std::cout.tie(0)const int N = 6e5 + 5;
const int INF = 1e18;
const int MOD = 998244353;
// const int MOD=1e9+7;
// const int MOD=100003;
const int maxn=5e5+10;int dp[N][23];
std::vector<int> mp[N];void dfs(int x,int fa){for(auto i : mp[x]){if(i==fa) continue;dfs(i,x);for(int j=1;j<=20;j++){int ned=INF;for(int k=1;k<=20;k++){if(j==k) continue;ned=std::min(ned,dp[i][k]);}if(ned!=INF){dp[x][j]+=ned;}}}
}void solve(){int n;std::cin >> n;std::vector<int> ve(n+3);for(int i=1;i<=n;i++){std::cin >> ve[i];for(int j=1;j<=20;j++){dp[i][j]=ve[i]*j;}mp[i].clear();}for(int i=0;i<n-1;i++){int x,y;std::cin >> x >> y;mp[x].push_back(y);mp[y].push_back(x);}dfs(1,0);std::cout << *min_element(dp[1]+1,dp[1]+20) << '\n';}signed main()
{// IOS;int t = 1;std::cin >> t;while (t--){solve();}
}