F - 树上染色

Des

给定一棵树,要求在树上把 $m$ 个点染成黑色,并有价值函数

$$v(E’) = \sum_{v_1,v_2\in E’} dis(v_1,v_2) + \sum_{v_1,v_2\in E-E’} dis(v_1,v_2)$$

求一种染色方式使得最大化 $v(E’)$。

Sol

仍然考虑动态规划。

$f_{u,k}$ 表示 $u$ 子树内选择 $k$ 个 对答案的贡献。考虑这和 $f_{u,k}$ 表示 $u$ 子树内选择 $k$ 个 的价值 有什么不同。

对于答案的贡献,我们是默认在 $u$ 子树之外已经选择了 $m-k$ 个其他的黑色节点的,因此对于边 $u\to v$,若有 $v$ 子树内已经选择了 $k$ 个,那么边 $u\to v$ 经过的次数就是 $k\times(m-k)$ 次,因此对答案的贡献就是 $w\times k\times (m-k)$。同理可以计算得到白点的贡献。

而价值指的是 $u$ 子树内选择 $k$ 个,而 $v$ 子树内选择 $k’$ 个的贡献,这是不好被统计的。

由第一个设的状态,我们有转移方程:

$$f_{u,j} = \max{f_{v,k} + f_{u,j-k} + w\times k\times (m-k) + w\times (sz_v-k)\times (n-m-sz_v+k)}$$

但是这样显然会 $\text{TLE}$。这是因为我们需要 $DFS$,并且每个点枚举一个 $j$ 和一个 $k$,复杂度 $O(nk^2)$。无法接受。

我们做一个上下界优化。显然,$j$ 的上下界是 $[0,\min(m,sz_u)]$,其中 $u$ 是已经遍历到的 $u$ 子树的大小。而 $k$ 的范围是 $[\max(j-sz_u+sz_v,0),\min(sz_v,m)]$。因此,式子就变成了:

$$f_{u,j} = \max_{j=0}^{\min(m,sz_u)}\max_{k=\max(j-sz_u+sz_v,0)}^{\min(sz_v,m)}{f_{v,k} + f_{u,j-k} + w\times k\times (m-k) + w\times (sz_v-k)\times (n-m-sz_v+k)}$$

为啥复杂度是对的前几篇题解写得也已经很明确了。这里需要注意的是 $j$ 需要倒序枚举,否则答案会不正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>

#include<bitset>
#include<set>

#include<deque>
#include<cassert>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<vector>
#include<random>

#define fi first
#define se second
#define pb push_back
#define mk make_pair
#define DBG cerr << __LINE__ << ' ' << __FUNCTION__ << endl

#define DRE default_random_engine
#define UID uniform_int_distribution
#define y0 Y0
#define y1 Y1
#define endl '\n'

#define pi acos(-1)
#define eps (1e-8)
#define null nullptr

using namespace std;

#define int long long
const int INF = 0x3f3f3f3f;
typedef pair<int,int> PII;
typedef pair<int,PII> PIII;
const int N = 2e3 + 10;
const int M = 5e2 + 10;

int f[N][N], n, m, siz[N];
vector<PII> g[N];

void solve(int x,int fa){
siz[x] = 1;
for(PII y:g[x]){
if(y.fi == fa) continue;
solve(y.fi, x);
siz[x] += siz[y.fi];
for(int j = max(siz[x], m); j>=0; --j)
for(int k = max(j - siz[x] + siz[y.fi], 0ll); k <= min(j, siz[y.fi]); ++k)
f[x][j] = max(f[x][j], f[y.fi][k] + f[x][j-k] + y.se * k * (m-k) + y.se * (siz[y.fi]-k) * (n-m-siz[y.fi]+k));
}
}

signed main(){
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin>>n>>m;
for(int i=1,x,y,z;i<n;++i) cin>>x>>y>>z, g[x].pb(mk(y,z)), g[y].pb(mk(x,z));
solve(1, 0);
cout<<f[1][m]<<endl;
return 0;
}