非常好题目,结论差点就真了。
手玩几棵树的构造过程,会发现我们每次都是先从一个点开始,点亮其子树所有点,然后到父亲,点亮父亲子树所有点,如此往复直到某个拐点
,又往下走到某个点 ,熄灭在  子树但不在 
子树的所有点,如此往复,到达某个点时停下。
那么,这个过程相当于在树上走出一条链,那么在这条链上的点的限制都能得到满足,但代价是
链上深度最浅的点的子树大小。
设链的端点为 ,需要注意的是,链并不一定是形如  的,可能会在到达
后,又往上走一段,又折返下来,即找到某个  的祖先 ,走出形如  的路径。此时代价为 。
那么原问题转化为,找到若干条这样的路径,使得树上每个点被覆盖至少一次,最小化代价和。
因为  并不一定是  ,所以我们难以知道以 
为端点的链会在哪里合并起来。不如直接对链上的每条边计算代价,而不是关注
 具体是多少。具体地,对于  和  的边,代价都是 。对于一条链的端点,代价就是 。一条链就由两个部分组成:边被经过
 次和  次的部分。
所以设  表示  子树内还有
条链的端点没有找到与其对应的端点(即这些链还处在”边被经过一次“的阶段), 有没有被至少一条链经过则用  表示。
 的初值就是 。
在合并  的 DP 数组和儿子  的 DP 数组时,分讨  边被经过  次(经过  次的情况可以调整到不超过  次)。那么有转移:
上述三类分别代表  经过
 次的转移。
于是本题就做完了,时间复杂度 。
| 12
 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
 
 | #include <bits/stdc++.h>using namespace std;
 template<typename T> inline void ckmin(T &x,const T &y) { if(x > y) x = y;}
 const int N = 2e5 + 5;
 int n;
 vector<int> G[N];
 int dp[N][2][2],sze[N];
 
 void dfs(int x,int fa) {
 sze[x] = 1;
 for(auto y : G[x]) if(y != fa) dfs(y,x),sze[x] += sze[y];
 int tmp[2][2];memset(tmp,0x3f,sizeof tmp);
 dp[x][0][0] = 0;dp[x][1][1] = sze[x];dp[x][0][1] = sze[x] + sze[x];
 dp[x][1][0] = 0x3f3f3f3f;
 for(auto y : G[x]) {
 if(y == fa) continue;
 for(int i = 0;i < 2;i++)
 for(int j = 0;j < 2;j++) {
 ckmin(tmp[i][j],dp[x][i][j] + dp[y][0][1]);
 for(int k = 0;k < 2;k++)
 ckmin(tmp[i ^ 1][j | k],dp[x][i][j] + dp[y][1][k] + (sze[x] - sze[y])),
 ckmin(tmp[i][j | k],dp[x][i][j] + dp[y][0][k] + 2 * (sze[x] - sze[y]));
 }
 for(int i = 0;i < 2;i++)
 for(int j = 0;j < 2;j++)
 dp[x][i][j] = tmp[i][j],tmp[i][j] = 0x3f3f3f3f;
 }
 }
 int main() {
 cin >> n;
 for(int i = 2,x;i <= n;i++) cin >> x,G[x].push_back(i);
 dfs(1,0);
 cout << dp[1][0][1] << endl;
 return 0;
 }
 
 |