test20260729
\(\text{P3243}\)
乐子签到题,优先队列加拓扑求个字典序最大,反过来就好了
\(\text{P2279}\)
建最少的点,每个点的笼罩范围是距离在2以内,要把所有点都笼罩到
树形\(dp\)
首先你在 \(dp\) 的时候,不用考虑这个点关于父亲,和祖父笼罩到的情况因为父亲和组父回来考虑这个点
所以我们\(dp\) 的时候就可以只考虑子树内的点了
设 \(f_{u,0/1}\) 表示在 \(u\) 这个点建或者不建的时,使得\(u\) 整个子树都被笼罩到时的最小代价
\(f_{u,1}\)
当在 \(u\) 这个点建设时,对于\(u\) 的子树内部,它可以笼罩到\(u\) 自己,和\(u\)的所有儿子,和孙子
这样的话,笼罩 \(u\) 就划分为两个部分的代价
- \(u\) 和它的儿子孙子,代价是1,也就是在 \(u\) 这里建一个站
- 所有\(u\)的曾孙形成的子树,这里的代价记为 \(sss_i\)
表示使得 \(i\) 的所有曾孙形成的子树满足的最小代价为了转移这个 \(sss_i\),我们再记\(ss_i\) 表示\(i\) 的所有孙子形成的子树满足条件的最小代价;\(s_i\)表示\(i\)的所有儿子形成的子树满足条件的最小代价
然后就很容易转移了,详细见于代码
所以 \(f_{u,1} = sss_u + 1\)
\(f_{u, 0}\)
一个点不建,那么为了笼罩这一个点,它的儿子中或者孙子中至少有一个建
-
至少一个儿子建:记 \(h_i\) 表示至少有一个儿子建了,使得 \(i\) 这个子树都被覆盖到的最小代价
那么考虑一个点 \(v\in son(u)\),我们再\(v\) 这个点建,那么\(v\)这个点本身可以笼罩到哪些点?v子树内的一部分以及u的所有儿子
所以我们就把代价分成了两个部分- v 子树, u,和所有 \(son(u)\)
代价:\(min(f_{v,0},f_{v,1})\) - 其他部分:u除了v以外的儿子 的儿子构成的子树
代价:\(\sum_{w\in son(u)且w\ne v}^{s_w}\)
所以把这个部分取最小值就是 \(h_u\)
- v 子树, u,和所有 \(son(u)\)
-
至少一个孙子建了
这里有点绕,有一个孙子建了,也就是 \(u\) 的一个儿子 \(v\) 的至少一个儿子建了多读两遍理解一下
那我们依然枚举这个 \(v\),把代价分为两个部分
- v子树和u
在v的一个儿子建,两个距离,也就是刚好向上能找到了u
那么你看了一下发现 \(v\) 至少一个儿子建的最小值就是 \(h_v\) ! 而我们前面定义 \(h\) 的时候的定义,把整棵子树的代价都算在了\(h\) 里面,所以这里的 \(h_v\) 就已经覆盖到了 v子树和u,这一部分的代价就是 \(h_v\) - 剩余u除了v以外的其他儿子 \(w\) 笼罩不到,那么wz覆盖它的子树的最小代价就是 \(min(f_{w,0},f_{w,1})\) (只要求覆盖这一个子树,并没有其他限制)
孙子建总代价 \(h_v + \sum_{w\in son(u)且w\ne v}^{min(f_{w,0},f_{w,1})}\)
- v子树和u
这样就得到了 \(f_{u,0}\) 的转移,就是儿子建和孙子建的最小值
最终的答案就是 \(min(f_{1,0},f_{1,1})\)
code
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5010;
int n;
vector<int> G[N];
int fa[N];
int f[N][2], h[N];
int s[N], ss[N], sss[N];
void dfs(int u) {
h[u] = 1e18;
f[u][0] = 1e18;
for(int v : G[u]) {
if(v == fa[u]) continue;
dfs(v);
}
for(int v : G[u]) { // 儿子建
if(v == fa[u]) continue;
int res = 0;
for(int w : G[u]) {
if(w == fa[u]) continue;
if(w == v) continue;
// res += min(s[w], min(f[w][0], f[w][1]));
res += s[w];
}
h[u] = min(h[u], f[v][1] + res);
}
f[u][0] = h[u];
for(int v : G[u]) { // 孙子建
if(v == fa[u]) continue;
int res = 0;
for(int w : G[u]) {
if(w == v) continue;
if(w == fa[u]) continue;
res += min(f[w][0], f[w][1]);
}
f[u][0] = min(f[u][0], h[v] + res);
}
s[u] = ss[u] = sss[u] = 0;
for(int v : G[u]) {
if(v == fa[u]) continue;
s[u] = s[u] + min(f[v][0], f[v][1]);
ss[u] = ss[u] + s[v];
sss[u] = sss[u] + ss[v];
}
// s[u] = min(s[u], h[u]);
f[u][1] = sss[u] + 1;
s[u] = min(s[u], f[u][0]);
s[u] = min(s[u], f[u][1]);
ss[u] = min(ss[u], f[u][0]);
ss[u] = min(ss[u], f[u][1]);
sss[u] = min(ss[u], f[u][0]);
sss[u] = min(ss[u], f[u][1]);
}
signed main() {
// freopen("fire.in","r",stdin);
// freopen("fire.out","w",stdout);
ios::sync_with_stdio(0);
cin >> n;
for(int i = 2; i <= n; i ++) {
cin >> fa[i];
G[fa[i]].push_back(i);
}
dfs(1);
cout << min(f[1][0], f[1][1]) << '\n';
// cout << "Fi0 : ";
// for(int i = 1; i <= n; i ++) cout << f[i][0] << ' '; cout << '\n';
// cout << "Fi1 : ";
// for(int i = 1; i <= n; i ++) cout << f[i][1] << ' '; cout << '\n';
// cout << "hi : ";
// for(int i = 1; i <= n; i ++) cout << h[i] << ' '; cout << '\n';
// cout << "si : ";
// for(int i = 1; i <= n; i ++) cout << s[i] << ' '; cout << '\n';
// cout << "ssi : ";
// for(int i = 1; i <= n; i ++) cout << ss[i] << ' '; cout << '\n';
// cout << "sssi : ";
// for(int i = 1; i <= n; i ++) cout << sss[i] << ' '; cout << '\n';
return 0;
}

浙公网安备 33010602011771号