[ SCOI 2015 ] 小凸玩密室

题目

Luogu
LOJ
Acwing

思路 1

1.png
2.png
3.png
4.png
5.png
6.png

代码 1

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define int long long
using namespace std;
const int N = 200010, INF = 2e18;
// 定义 A 为点权, B[i] 为 i 连向父亲的点权
int n, A[N], B[N], dep[N];
// 由于开数组需要 n * n 的空间, 但是实际上不需要那么多, 所以动态加状态
vector<int> f[N], g[N], dis[N];
inline void update(int u) {
	// 枚举所有祖先
	for (int i = u; i >= 1; i >>= 1)
		f[i].push_back(0), g[i].push_back(0), 
		dis[i].push_back(dep[u] - dep[i]);
}
#define ls (u << 1)
#define rs (u << 1 | 1)
void DFS(int u) {
	// 没有左儿子, 说明 u 是叶子节点
	if (ls > n)  { update(u); return; }
	dep[ls] = dep[u] + B[ls], DFS(ls);
	// 此时只有一个左儿子, 没有右儿子
	if (rs > n) {
		update(u);
		f[u][0] = A[ls] * B[ls], f[u][1] = INF;          
		g[u][0] = f[u][0], g[u][1] = A[u] * B[ls]; 
		return;
	}
	dep[rs] = dep[u] + B[rs], DFS(rs);
	
	int sz = f[ls].size(); // 左儿子的叶子节点个数, 后面有用
	int fls = INF, frs = INF, gls = INF, grs = INF;
	// d[u][rs] = B[rs], d[u][ls] = B[ls];
	// 其中 d[ls][y] 由于 ls存的是子树, 没存 y, 所以变型等于 dis[u][y] + B[ls] 
	// d[rs][y] 同理
	
	// 这里把状态转移方程重写一遍, 避免读者上下翻看
	
	// f[u][x] = f[ls][x] + min { B[rs] * A[rs] + (dis[u][y] + B[ls]) * A[ls] + f[rs][y] }
	// 				  frs = min { B[rs] * A[rs] + (dis[u][y] + B[ls]) * A[ls] + f[rs][y] };
	
	// f[u][x] = f[rs][x] + min { B[ls] * A[ls] + (dis[u][y] + B[rs]) * A[rs] + f[ls][y] }
	//                fls = min { B[ls] * A[ls] + (dis[u][y] + B[rs]) * A[rs] + f[ls][y] }
	
	for (int y = 0; y < f[u].size(); y++)
		if (y < sz) fls = min(fls, B[ls] * A[ls] + (dis[u][y] + B[rs]) * A[rs] + f[ls][y]);
		else frs = min(frs, B[rs] * A[rs] + (dis[u][y] + B[ls]) * A[ls] + f[rs][y - sz]);
			// y 在右子树  因为右子树大小限制, y 都要减去 sz 
	for (int x = 0; x < f[u].size(); x++) 
		if (x < sz) f[u][x] = f[ls][x] + frs;
		else f[u][x] = f[rs][x - sz] + fls;
	
	// g[u][x] = f[ls][x] + min { g[rs][y] + dis[u][y] * A[u] + B[ls] * A[ls] }
	// 				  grs = min { g[rs][y] + dis[u][y] * A[u] + B[ls] * A[ls] }
	
	// g[u][x] = f[rs][x] + min { g[ls][y] + dis[u][y] * A[u] + B[rs] * A[rs] }
	//  			  gls = min { g[ls][y] + dis[u][y] * A[u] + B[rs] * A[rs] }
	
	for (int y = 0; y < f[u].size(); y++)
		if (y < sz) gls = min(gls, g[ls][y] + dis[u][y] * A[u] + B[rs] * A[rs]);
		else grs = min(grs, g[rs][y - sz] + dis[u][y] * A[u] + B[ls] * A[ls]);
	for (int x = 0; x < f[u].size(); x++) 
		// f[u][x] 即为漏的那种情况
		if (x < sz) g[u][x] = min(f[u][x], f[ls][x] + grs);
		else g[u][x] = min(f[u][x], f[rs][x - sz] + gls);
}
signed main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> A[i];
	for (int i = 2; i <= n; i++) cin >> B[i];
	DFS(1);
	long long res = 2e18;
	for (int x = 0; x < g[1].size(); x++) res = min(g[1][x], res);
	cout << res << endl;
	return 0;
}

思路 2

7.png
8.png
9.png
10.png

代码 2

#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
using namespace std;
const int N = 200010, INF = 2e18;
int n, m, layer[N], A[N], B[N], d[N][20];
int f[N][20], g[N][20];
#define ls (i << 1) 
#define rs (i << 1 | 1)
int DP() {
	for (int i = n; i >= 1; i--) {
		for (int j = 1; j <= layer[i]; j++) {
			int brother = (i >> j - 1) ^ 1;
			if (ls > n) f[i][j] = d[i][j] * A[i >> j], 
						g[i][j] = (d[i][j] + d[brother][1]) * A[brother];
			else if (rs > n) f[i][j] = d[ls][1] * A[ls] + f[ls][j + 1], 
							 g[i][j] = d[ls][1] * A[ls] + g[ls][j + 1];
			else f[i][j] = min(d[ls][1] * A[ls] + g[ls][1] + f[rs][j + 1], 
							   d[rs][1] * A[rs] + g[rs][1] + f[ls][j + 1]), 
				 g[i][j] = min(d[ls][1] * A[ls] + g[ls][1] + g[rs][j + 1], 
				 			   d[rs][1] * A[rs] + g[rs][1] + g[ls][j + 1]);
		}
	}
	int res = INF;
	for (int i = 1; i <= n; i++) {
		int temp = f[i][1];
		int x = i, y = i ^ 1, fa = x >> 1;
		for (int j = 1; j <= layer[i]; j++) {
			if (y <= n) temp += d[y][1] * A[y] + f[y][2];
			else temp += d[fa][1] * A[fa >> 1];
			x = fa, y = x ^ 1, fa = x >> 1;
		}
		res = min(res, temp);
	}
	return res;
}
signed main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> A[i];
	for (int i = 2; i <= n; i++) cin >> B[i];
	// 找层
	for (int i = 1; i <= n; i++) layer[i] = layer[i >> 1] + 1;
	// 算距离
	for (int i = 2; i <= n; i++) 
		for (int j = 1; j <= layer[i]; j++)
			d[i][j] = d[i >> 1][j - 1] + B[i];
	cout << DP() << endl;
	return 0;
}
posted @ 2021-04-08 16:12  Protein_lzl  阅读(72)  评论(0编辑  收藏  举报