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\),应该就想到状态了吧。。。

posted @ 2018-12-22 22:58  wawawa8  阅读(132)  评论(0编辑  收藏  举报