1. 程式人生 > >bzoj 5290 [HNOI2018] 道路

bzoj 5290 [HNOI2018] 道路

bzoj 5290 [HNOI2018] 道路

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\),應該就想到狀態了吧。。。