bzoj 5290 [HNOI2018] 道路
阿新 • • 發佈:2018-12-22
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\),應該就想到狀態了吧。。。