dp 可并堆 uoj205【APIO2016】Fireworks

2021-02-09 09:18

阅读:699

http://uoj.ac/problem/205

这题好强啊
设状态\(f[id][t]\) 为一个点子树内所有的点在\(t\)时间完成的最小的代价
通过观察可以发现
\(f\)值形成了一个下凸包
在区间\([l, r]\)取最小值
考虑暴力转移
更新父节点答案
以及暴力合并上一段凸包
复杂度\(O((n + m) ^ 2)\)

列出转移方程之后
\(x\)的斜率分别为\(-1, 0, 1\)
发现本质上是对一段凸包向上平移
再新增两个拐点(凸包上的拐点)

发现这个凸包的形态只和拐点的数量有关
每经过一个拐点 斜率就\(+1\)
我们要维护\(k 的凸包
就是把所有斜率大于\(0\)的拐点删掉

\(f[rt]\)的凸包与\(y\)轴交点已知
\(\sum\)边权
且位置为x的可以通过拐点的数量得出
因此我们维护拐点就可以了

那么如何合并呢?
用可并大根堆
每一次把\(k\)大于0的拐点筛掉
此时凸包最右侧的斜率就是他的孩子的个数
所以只需删去孩子个数个拐点
然后再加两个就可以了

复杂度\(O((n + m) log (n + m))\)

#include
#define int long long
#define fo(i, n) for(int i = 1; i 
#define out(x) cerr \n"
#define type(x) __typeof((x).begin())
#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); ++ it)
using namespace std;
// by piano
templatetypename tp> inline void read(tp &x) {
  x = 0;char c = getchar(); bool f = 0;
  for(; c '0' || c > '9'; f |= (c == '-'), c = getchar());
  for(; c >= '0' && c '9'; x = (x 3) + (x 1) + c - '0', c = getchar());
  if(f) x = -x;
}
templatetypename tp> inline void arr(tp *a, int n) {
  for(int i = 1; i " ";
  puts("");
}
const int N = 6e5 + 233;
int n, m, fa[N], w[N], sz[N], ans = 0;
int key[N], fix[N], sa = 0, L[N], R[N], rt[N];
inline int nw(int val) {
  ++ sa; L[sa] = R[sa] = 0;
  key[sa] = val;
  fix[sa] = rand();
  return sa;
}

inline int make(int x, int y) {
  if(!x || !y) return x | y;
  if(key[x] if(fix[x] else 
    L[x] = make(L[x], y);
  return x;
}

inline void pop(int x) {
  rt[x] = make(L[rt[x]], R[rt[x]]);
}

inline void dfs(int x) {
  if(!x) return ;
  cout " " "\n";
  dfs(L[x]); dfs(R[x]);
}

main(void) {
  srand(20021214);
  read(n); read(m);
  int all = n + m;
  for(int i = 2; i for(int i = all; i >= 2; i --) {
    int l = 0, r = 0;
    if(sz[i]) {
      while(-- sz[i]) pop(i);
      r = key[rt[i]]; pop(i);
      l = key[rt[i]]; pop(i);
    }
    l = nw(l + w[i]);
    r = nw(r + w[i]);
    rt[i] = make(rt[i], make(l, r));
    rt[fa[i]] = make(rt[fa[i]], rt[i]);
  }
  while(sz[1] --) pop(1);
  while(rt[1]) {
    ans -= key[rt[1]];
    pop(1);
  }
  cout "\n";
}


评论


亲,登录后才可以留言!