Loj2009「SCOI2015」小凸玩密室

仔细观察以后可以发现,关于电灯有以下几个性质:

1
根节点不是1,也就是每个点都可能为根节点,这就意味着不能用O(N^2)的做法,只能考虑O(NlogN)或者O(N)

2
所有点亮的灯都是连在一起的

3
对于一个已经点亮的点,它的左右子树只有3种可能:没有染色,已经全部染完,正在染色

4
当你准备将一个点染色的时候,上一个染色的点可能是它的父亲或者它父亲拥有的另外一棵子树的叶子节点

根据这几个性质,可以想到dp状态:
dp[i][j][0]表示将第i个点的子树全部点亮并且上去点亮i的第j个祖先所需的最小费用
dp[i][j][1]表示将第i个点的子树全部点亮并且上去点亮i的第j个祖先的另外一个儿子的最小费用
状态转移分三种情况:没有儿子,一个儿子,两个儿子

将dp求出来以后,我们就要算结果了
先枚举每一个点,将这个点看作根节点,然后一层一层往上跳,计算结果,记录最小值。

#include 
#include 
#include 
#include 
#include 
using namespace std;
template<typename T>inline void read(T &x) {
	x = 0; int f = 0; char s = getchar();
	while (!isdigit(s)) f |= s=='-', s = getchar();
	while ( isdigit(s)) x = x * 10 + s - 48, s = getchar();
	x = f ? -x : x;
}
typedef long long LL;
const int N = 2e5 + 6, M = 23;
int n, Log[N]; LL f[N][M][2], d[N][M], val[N];

inline void Prepare() {
	read(n);
	for (int i = 1; i <= n; i++) read(val[i]);
	for (int i = 1; i < n; i++) read(d[i + 1][1]);
	for (int i = 1; i <= n; i++) Log[i] = Log[i >> 1] + 1;
	for (int i = n; i; i--)
		for (int j = 2; j < Log[i]; j++)
			d[i][j] = d[i][j - 1] + d[i >> (j - 1)][1];
}

inline void Dp() {
	for (int i = n; i; i--)
		for (int j = 1; j <= Log[i]; j++) {
			if (2 * i > n) {
				int w = (i >> (j - 1)) ^ 1;
				f[i][j][0] = d[i][j] * val[i >> j];
				f[i][j][1] = (d[i][j] + d[w][1]) * val[w];
			}
			else if ((i << 1 | 1) > n) {
				int ls = i << 1;
				f[i][j][0] = f[ls][j + 1][0] + d[ls][1] * val[ls];
				f[i][j][1] = f[ls][j + 1][1] + d[ls][1] * val[ls];
			}
			else {
				int ls = i << 1, rs = i << 1 | 1;
				f[i][j][0] = min(f[ls][j + 1][0] + f[rs][1][1] + d[rs][1] * val[rs],
								 f[rs][j + 1][0] + f[ls][1][1] + d[ls][1] * val[ls]);
				f[i][j][1] = min(f[ls][j + 1][1] + f[rs][1][1] + d[rs][1] * val[rs],
								 f[rs][j + 1][1] + f[ls][1][1] + d[ls][1] * val[ls]);
			}
		}
	LL ans = 0x3f3f3f3f3f3f3f3f;
	for (int i = 1; i <= n; i++) {
		LL tmp = f[i][1][0];
		for (int x = i >> 1, pre = i, t = 1; t <= Log[i]; t++, pre = x, x >>= 1) {
			int w = pre ^ 1;
			if (w <= n) tmp += d[w][1] * val[w] + f[w][2][0];
			else tmp += d[x][1] * val[x>>1];
		}
		ans = min(ans, tmp);
	}
	cout << ans << endl;
}

int main() {
	Prepare();
	Dp();
	return 0;
}

你可能感兴趣的:(loj,题解,各省省选)