SCOI2015 小凸玩密室
[SCOI2015] 小凸玩密室
简化题意
给定一颗 完全二叉树 ,每个节点上有一个灯,你可以自由选择点亮的第一个灯,之后所有点亮的灯必须在任一时刻形成连通块,且点一个位置的灯后必须点亮其子树所有点的灯。
现在每个点和每个边都有权值 \(a_i \ b_j\) , 从点亮 \(i\) 到点亮 \(j\) 的费用是 \(dis(i , j) \times a_j\) , 问点亮子树的最小代价。 \(n \le 2 \times 10^5\)
题解
感觉这种题都是一眼 DP 吧。
先随便设一个状态 \(dp_{i , j , k}\) 表示以 \(i\) 为根的子树从 \(j\) 开始到 \(k\) 结束的最小价值。
先认为 \(j\) 属于左子树,那分几种情况来考虑:
-
首先从 \(i\) 开始点灯的话,那么就要考虑先跑左子树还是先跑右子树。假设先跑左边,那么跑完之后肯定先跑 \(i\) 的右儿子。先跑右同理。那么这时我需要枚举一个起点。
-
先从左或右子树中任意一个位置开始,还是假设从左边开始,那么我需要枚举一个终点,跑完这个点之后跑 \(i\) , 再去跑右儿子,那么这个时候需要在左子树中枚举起终点。
我们发现起点不会对之后的转移造成任何影响,而对于一个点 \(i\) 我们也只需要到其父亲节点最小的价值和到其兄弟节点最小的价值。
那此时怎么做就很明了了,作为一个完全二叉树,枚举儿子显然 \(\mathcal{O}(n \log n)\)。
然后对于 \(n\) 是奇数时显然最后的终点是叶子结点。
但是 \(n\) 为偶数不一定,这个特判一下。
设 \(dp_i\) 表示从目前的根节点出发,到达 \(i\) 为结尾的最小代价。
\(f_{i , 0}\) 表示从 \(i\) 子树中任意一点出发,到达 \(i\) 父节点的最小代价。
\(f_{i , 1}\) 表示从 \(i\) 子树任意一点出发,到达 \(i\) 兄弟节点最小代价。
Code
CODE
#include <bits/stdc++.h>
typedef long long ll ;
using namespace std ;
const int N = 2e5 + 100 ;
int n , a[N] ; ll b[N] , f[N][2] , dp[N] , sum[N] ;
vector <int> son[N] ;
#define lson(x) (x << 1)
#define rson(x) (x << 1 | 1)
void dfs_son(int x) {
if (lson(x) > n) son[x].push_back(x) ;
if (lson(x) <= n) {
sum[lson(x)] += sum[x] ;
dfs_son(lson(x)) ;
for (auto j : son[lson(x)]) son[x].push_back(j) ;
}
if (rson(x) <= n) {
sum[rson(x)] += sum[x] ;
dfs_son(rson(x)) ;
for (auto j : son[rson(x)]) son[x].push_back(j) ;
}
}
void dfs(int x) {
if (lson(x) > n) {
dp[x] = 0 ;
f[x][0] = (sum[x] - sum[x >> 1]) * a[x >> 1] ;
f[x][1] = (sum[x ^ 1] + sum[x] - 2 * sum[x >> 1]) * a[x ^ 1] ;
return ;
} else if (rson(x) > n) {
dp[lson(x)] = (sum[lson(x)] - sum[x]) * a[lson(x)] ;
f[x][0] = min((sum[lson(x)] - sum[x]) * a[lson(x)] + (sum[lson(x)] - sum[x >> 1]) * a[x >> 1] , (sum[lson(x)] - sum[x]) * a[x] + (sum[x] - sum[x >> 1]) * a[x >> 1]) ;
f[x][1] = dp[lson(x)] + (sum[lson(x)] + sum[x ^ 1] - 2 * sum[x >> 1]) * a[x ^ 1] ;
return ;
}
dfs(lson(x)) , dfs(rson(x)) ; f[x][0] = f[x][1] = 1e18 ;
if (x == 1) {
ll ans = 1e18 ;
for (auto j : son[rson(x)]) {
ans = min({ans ,
(- sum[x] + sum[lson(x)]) * a[lson(x)] + f[lson(x)][1] + dp[j] + (sum[x >> 1] - sum[j]) * a[x >> 1] ,
f[lson(x)][0] + (- sum[x] + sum[rson(x)]) * a[rson(x)] + dp[j]}) ;
}
for (auto j : son[lson(x)]) {
ans = min({ans ,
(- sum[x] + sum[rson(x)]) * a[rson(x)] + f[rson(x)][1] + dp[j] + (sum[x >> 1] - sum[j]) * a[x >> 1] ,
f[rson(x)][0] + (- sum[x] + sum[lson(x)]) * a[lson(x)] + dp[j]}) ;
}
cout << ans << '\n' ;
return ;
}
for (auto j : son[rson(x)]) {
f[x][0] = min({f[x][0] ,
(- sum[x] + sum[lson(x)]) * a[lson(x)] + f[lson(x)][1] + dp[j] + (- sum[x >> 1] + sum[j]) * a[x >> 1] ,
f[lson(x)][0] + (- sum[x] + sum[rson(x)]) * a[rson(x)] + dp[j] + (sum[j] - sum[x >> 1]) * a[x >> 1]
}) ;
f[x][1] = min({f[x][1] ,
(- sum[x] + sum[lson(x)]) * a[lson(x)] + f[lson(x)][1] + dp[j] + (sum[x ^ 1] + sum[j] - 2 * sum[x >> 1]) * a[x ^ 1]
}) ;
}
for (auto j : son[lson(x)]) {
f[x][0] = min({f[x][0] ,
(- sum[x] + sum[rson(x)]) * a[rson(x)] + f[rson(x)][1] + dp[j] + (- sum[x >> 1] + sum[j]) * a[x >> 1] ,
f[rson(x)][0] + (- sum[x] + sum[lson(x)]) * a[lson(x)] + dp[j] + (sum[j] - sum[x >> 1]) * a[x >> 1]
}) ;
f[x][1] = min({f[x][1] ,
(- sum[x] + sum[rson(x)]) * a[rson(x)] + f[rson(x)][1] + dp[j] + (sum[x ^ 1] + sum[j] - 2 * sum[x >> 1]) * a[x ^ 1]
}) ;
}
for (auto j : son[rson(x)]) dp[j] = (- sum[x] + sum[lson(x)]) * a[lson(x)] + f[lson(x)][1] + dp[j] ;
for (auto j : son[lson(x)]) dp[j] = (- sum[x] + sum[rson(x)]) * a[rson(x)] + f[rson(x)][1] + dp[j] ;
}
signed main() {
ios::sync_with_stdio(0) , cin.tie(0) , cout.tie(0) ;
cin >> n ;
for (int i = 1 ; i <= n ; ++ i) cin >> a[i] ;
for (int i = 2 ; i <= n ; ++ i) cin >> sum[i] ;
dfs_son(1) ;
dfs(1) ;
}