Loading

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\) 属于左子树,那分几种情况来考虑:

  1. 首先从 \(i\) 开始点灯的话,那么就要考虑先跑左子树还是先跑右子树。假设先跑左边,那么跑完之后肯定先跑 \(i\) 的右儿子。先跑右同理。那么这时我需要枚举一个起点。

  2. 先从左或右子树中任意一个位置开始,还是假设从左边开始,那么我需要枚举一个终点,跑完这个点之后跑 \(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) ; 
}

结尾撒花!

posted @ 2024-10-28 21:28  HANGRY_Sol&Cekas  阅读(18)  评论(1编辑  收藏  举报