题解:P4539 [SCOI2006] zh_tree
lcfollower · · 题解
模拟赛题,只推了一点式子,忘记了区间背包和中序遍历的关系,然后部分分也挂了,所以得了
以下内容有点参考课件。
观察到根节点深度为
首先看到式子
因为
因为
于是我们设
细节:
-
我们规定
f_{i ,i - 1} = 0 ,表示没有贡献。 -
我们枚举根节点
k ,为了满足中序遍历的限制,于是将区间划分为[i ,k - 1] 与[k + 1 ,j] 。 -
划分的区间的子树的根,深度为
dep + 1 。
时间复杂度为
那么我们能不能删除
是可以的,我们可以只计算当前子树的价值。
我们设
先看一下转移:
注意到后面的
可以看图解:
假设我们一开始知道如下两个子树的价值总和:
然后我们让
这样答案是
总时间复杂度为
# include <bits/stdc++.h>
# define int long long
# define up(i, x, y) for (int i = x; i <= y; i++)
# define dn(i, x, y) for (int i = x; i >= y; i--)
# define inf 1e18
using namespace std;
inline int read(){int x = 0, f = 0;char ch = getchar();while (!isdigit(ch)) {f |= (ch == '-');ch = getchar();}while (isdigit(ch))x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();return f ? -x : x;}
inline void write(int x){if (x < 0)putchar('-'), x = -x;if (x > 9)write(x / 10);putchar(x % 10 | 48);}
inline void writeln(int x){write(x), putchar('\n');}
inline void writesp(int x){write(x), putchar(' ');}
const int N = 305;
int n;
double k ,c ,d[N];
double f[N][N];
namespace lolcrying {
signed main () {
n = read () ; cin >> k >> c;
double sum = 0;
up (i ,1 ,n) cin >> d[i] ,sum += d[i];
up (i ,1 ,n) up (j ,1 ,n) f[i][j] = inf;
up (i ,1 ,n) f[i][i - 1] = 0 ,d[i] += d[i - 1];
up (len ,1 ,n) {//其实 f[i][i] 可以不初始化,也可以直接放到 DP 里,计算一下还是 d[i](这里指的不是前缀和的 d[i])。
up (i ,1 ,n - len + 1) {
int j = i + len - 1;
up (k ,i ,j) f[i][j] = min (f[i][j] ,f[i][k - 1] + f[k + 1][j]);//枚举根节点 k。
f[i][j] += d[j] - d[i - 1];//前缀和,写不写无所谓。
}
} printf ("%.3lf\n" ,(f[1][n] * k / sum + c));
return 0;
}
}
signed main () {
int T = 1;
while (T --) lolcrying :: main ();
return 0;
}