[HAOI2015]树上染色 | 树形dp
[HAOI2015]树上染色
https://ac.nowcoder.com/acm/problem/19996
题目大意
题目思路
看到树上问题就来补了
没想到还真是个好题
开始状态定义,定义代表以为根的子树,选择了个黑点,所达到的最大收益。
显然不可以转移,因为不知道其他的点黑点和白点选择情况,这里就有后效性了
所以考虑去除后效性【即不能对后面的状态产生影响】
显然直接计算距离是不可取的,但是对于两两之间的距离,在树上还可以用:
表示第i条边的边权,代表一段点下子数的大小
根据这个启发,可以将状态转移定义为边的转移 :代表以i为根的子树,选择了个黑点,子树下边权贡献最大和是多少。
因为,选择多少个黑点是确定的,所以对于一个子树下,选择了多少个黑点,就可以算出来,这个边的贡献。
然后转移一下就好了
Code:
/*** keep hungry and calm CoolGuang! ***/ //#pragma GCC optimize(3) #include <bits/stdc++.h> #include<stdio.h> #include<queue> #include<algorithm> #include<string.h> #include<iostream> #define debug(x) cout<<#x<<":"<<x<<endl; #define dl(x) printf("%lld\n",x); #define di(x) printf("%d\n",x); typedef long long ll; typedef unsigned long long ull; using namespace std; const ll INF= 1e16+7; const ll maxn = 2e6+700; const int M = 1e6+8; const ll mod= 1e15+7; const double eps = 1e-9; const double PI = acos(-1); template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}a=f*x;} ll a[maxn]; ///以i为根的子数,涂了k个黑点,所有边权和最小值 ll dp[2005][2005]; ll sz[2005],t[2005]; ll n,m,p; vector<pair<int,int>>v[maxn]; void dfs(int u,int fa){ sz[u] = 1; for(auto x:v[u]){ int e = x.first,w = x.second; if(e == fa) continue; dfs(e,u); for(int k=0;k<=m;k++) t[k] = -INF; for(int k=0;k<=min(m,sz[u]*1ll);k++){ for(int j=0;j<=min(m,sz[e]*1ll);j++){ if(k+j>m) break; ll bc = j,wc = sz[e]-j; t[k+j] = max(t[k+j],dp[u][k]+1ll*w*(bc*(m-bc)+1ll*wc*(n-m-wc))+dp[e][j]); } } for(int k=0;k<=m;k++) dp[u][k] = t[k]; sz[u] += sz[e]; } } int main(){ read(n);read(m); for(int i=1;i<=n-1;i++){ int x,y,w; read(x);read(y);read(w); v[x].push_back({y,w}); v[y].push_back({x,w}); } dfs(1,1); printf("%lld\n",dp[1][m]); return 0; }/** 1 6 1 1 1 2 2 1 3 2 4 2 5 2 ***/