bzoj 5290 [HNOI2018] 道路
bzoj 5290 [HNOI2018] 道路
Link
Solution
实际上是个大水题,但是一脸难题的样子
发现限制了深度小于等于 \(40\)
设计状态 \(f[i][x][y]\) 表示当前点为 \(i\),当前点到根的路径上有 \(x\) 条公路没有被翻修,\(y\) 条铁路没有被翻修
那么对于所有的叶子节点,就是所有的乡村,可以直接枚举 \(x, y\) 求出所有 \(f\) 的值,也就是 \(\text{dp}\) 的初值
然后转移就是枚举当前点连出去的两条边翻修了哪一条,\(f[i][x][y] = \min(f[ls][x][y]+ f[rs][x][y+1],f[ls][x+1][y]+f[rs][x][y])\)
Code
// Copyright lzt
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef std::pair<int, int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef std::pair<long long, long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i, j, k) for (register int i = (int)(j); i <= (int)(k); i++)
#define rrep(i, j, k) for (register int i = (int)(j); i >= (int)(k); i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__)
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch <= '9' && ch >= '0') {
x = 10 * x + ch - '0';
ch = getchar();
}
return x * f;
}
const int maxn = 20020;
int n;
int ls[maxn], rs[maxn];
int a[maxn << 1], b[maxn << 1], c[maxn << 1];
int num[maxn << 1][2];
ll f[maxn][44][44];
void dfs(int u) {
if (u >= n) return;
num[ls[u]][0] = num[u][0] + 1;
num[ls[u]][1] = num[u][1];
num[rs[u]][0] = num[u][0];
num[rs[u]][1] = num[u][1] + 1;
dfs(ls[u]); dfs(rs[u]);
}
inline ll calc(int i, int x, int y) {
if (i < n) return f[i][x][y];
else return c[i] * 1ll * (a[i] + x) * (b[i] + y);
}
void work() {
n = read();
rep(i, 1, n - 1) {
ls[i] = read(), rs[i] = read();
if (ls[i] < 0) ls[i] = -ls[i] + (n - 1);
if (rs[i] < 0) rs[i] = -rs[i] + (n - 1);
}
rep(i, n, n + n - 1) a[i] = read(), b[i] = read(), c[i] = read();
dfs(1);
rrep(i, n - 1, 1) {
rep(x, 0, num[i][0]) rep(y, 0, num[i][1]) {
f[i][x][y] = min(calc(ls[i], x, y) + calc(rs[i], x, y + 1),
calc(ls[i], x + 1, y) + calc(rs[i], x, y));
}
}
printf("%lld\n", f[1][0][0]);
}
int main() {
#ifdef LZT
freopen("in", "r", stdin);
#endif
work();
#ifdef LZT
Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
}
Review
状态比较特别
一般的状态都是转移方向为 \(y\) 转移到 \(x\) 的时候,\(x\) 对应的状态包含了 \(y\) 对应的状态,这道题是反过来的,儿子状态包含了父亲状态,所以可能比较难想清楚
看到深度 \(40\),点数 \(20000\),应该就想到状态了吧。。。