dp 可并堆 uoj205【APIO2016】Fireworks

http://uoj.ac/problem/205

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

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

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

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

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

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

#include<bits/stdc++.h>
#define int long long
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cerr << #x << " = " << x << "\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
template<typename 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;
}
template<typename tp> inline void arr(tp *a, int n) {
  for(int i = 1; i <= n; i ++)
    cout << a[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] < key[y]) swap(x, y);
  if(fix[x] < fix[y])
    R[x] = make(R[x], y);
  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 << key[x] << " " << x << "\n";
  dfs(L[x]); dfs(R[x]);
}

main(void) {
  srand(20021214);
  read(n); read(m);
  int all = n + m;
  for(int i = 2; i <= all; i ++) {
    read(fa[i]); read(w[i]);
    ans += w[i];
    sz[fa[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 << ans << "\n";
}

原文地址:https://www.cnblogs.com/foreverpiano/p/8560152.html

时间: 2024-07-31 07:35:28

dp 可并堆 uoj205【APIO2016】Fireworks的相关文章

UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】

题目分析: 不难发现可以用动态DP做. 题目相当于是要我求一条路径,所有与路径有交的链的代价加入进去,要求代价最大. 我们把链的代价分成两个部分:一部分将代价加入$LCA$之中,用$g$数组保存:另一部分将代价加在整条链上,用$d$数组保存. 这时候我们可以发现,一条从$u$到$v$的路径的代价相当于是$d[LCA(u,v)]+\sum_{x \in edge(u,v)}g[x]$. 如果是静态的,可以用树形DP解决. 看过<神奇的子图>的同学都知道,叶子结点是从它的儿子中取两个最大的出来,所

Apio2016 游记

Day 1 报到 早晨从长途汽车站出发,原来加上Lx只有6个人. 座位非常幸运地被换到了Lx旁边. 大概坐了5个多小时的车,刚开始教Lx玩魔方,转了一会之后她竟然晕车了!聊天时发现她挺萌的= = 就这么愉快的到了北京.竟然在长途车站去洗手间都要用身份证?! 下车之后大家围着地图看了好久最终决定先坐地铁再做公交,地铁的出口有个蜂窝,然后窝被蜜蜂蜇了!Lx帮我把蜂针拔出来之后十分认真的说:"诶你们看真的是一根针啊.." Yveh:完了你要截肢了怎么办... Fye:你是不是马上就要离开我们

【醒目】【业界偷懒】【Public】BZOJ题目一句话题解整理

就当是复习一下自己做过的题,顺便提供一个简要题解给大家看. 做题时候实在想不出来看一下一句话题解,可以有一个提示的作用又不至于一下子知道了全部浪费了一道题吧.. 部分题目(如我A过得大部分奶牛题)是别人拿我的账号做的,不提供题解. 可能会漏掉很多做过的题..因为可能点页数不小心点错了什么的 UPD.本来想把那些没写过但是知道题解的也写了..但是写完这些已经累死了QAQ 已AC的题目(数学题均不提供分析过程,公式): 1000:A+B 1001:平面图最小割,转对偶图最短路 1002:矩阵树定理,

[DP] 堆盒子问题

给一堆盒子,知道每个盒子的三围(长宽高),盒子正面朝你,不能旋转摆放,按照大的放在小的下面的原则堆起来,必须是 strictly larger,同样大小的盒子不行,问怎么样堆到最大的高度? 思路:动态规划 最优解一定是 max( {box_1 be the bottom}, {box_2 be the bottom}, ... , {box_n be the bottom} ),所以我们遍历所有的 box, 把每个box作为底部构建subproblem. 按说在subproblem {box_1

【bzoj1097】[POI2007]旅游景点atr 状压dp+堆优化Dijkstra

题目描述 FGD想从成都去上海旅游.在旅途中他希望经过一些城市并在那里欣赏风景,品尝风味小吃或者做其他的有趣的事情.经过这些城市的顺序不是完全随意的,比如说FGD不希望在刚吃过一顿大餐之后立刻去下一个城市登山,而是希望去另外什么地方喝下午茶.幸运的是,FGD的旅程不是既定的,他可以在某些旅行方案之间进行选择.由于FGD非常讨厌乘车的颠簸,他希望在满足他的要求的情况下,旅行的距离尽量短,这样他就有足够的精力来欣赏风景或者是泡MM了^_^.整个城市交通网络包含N个城市以及城市与城市之间的双向道路M条

HDU 4284 状压dp+spfa堆优化

题意: 给定n个点 m条无向边 d元. 下面m行表示每条边 u<=>v 以及花费 w 下面top 下面top行 num c d 表示点标为num的城市 工资为c 健康证价格为d 目标是经过给定的top个城市,当到达该城市时,必须马上购买该城市的健康证并打工赚钱(每个城市只打工1次) 问从1城市出发,最后回到1城市,能否收集到所有的健康证 思路: 由于top很小,所以状压dp dp[i][tmp]表示当前处于i点 经过城市的状态为tmp时 身上最多的钱. 首先对dis数组floyd 跑出最短路,

【BZOJ-4524】伪光滑数 堆 + 贪心 (暴力) [可持久化可并堆 + DP]

4524: [Cqoi2016]伪光滑数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 183  Solved: 82[Submit][Status][Discuss] Description 若一个大于R的整数J的质因数分解有F项,其最大的质因子为ak,并且满足ak^k≤N, ak<128,我们就称整数J为N-伪光滑数. 现在给出L,求所有整数中,第E大的N-伪光滑数. Input 只有一行,为用空格隔开的整数L和E. 2 ≤ N ≤ 10^1

【Luogu】P2254瑰丽华尔兹(堆优化DP)

题目链接 我也不知道为什么脑子一抽就想了个堆优化……然后贼慢…… 因为上午听不懂wys的电音专场(快速傅立叶变换),然后就做了这么一道题. 首先朴素DP很sb都能秒出.就是枚举时刻.位置(两维)然后转移. 观察发现这是O(TNM)的,可以通过50%的数据. 然后……(喂题目提示得太明显了吧)发现时间段只有200个,跟50%的T是一样的 这简直就是明说:“快往O(KNM)上想!” 然后我就不知道为啥想了个O(KNMlogn),qwq. 枚举时刻改为枚举时间段,每个时间段枚举位置用堆优化(其实应该用

校内胡策 埃罗芒阿老师 - 贪心 &amp; 堆 + EX难度 - DP

题目描述 埃罗芒阿老师是著名的插画家,她的工作是为电击文库出版的的书画插画.快要到截稿日了,埃罗芒阿老师还在水>_<埃罗芒阿突然发现自己还有一大堆插画没有完成,如果不能在截稿时间内完成是要扣工资的.于是埃罗芒阿老师把每个任务所需的时间和现在(0 时刻)距离每个任务截稿的时间记录了下来,想要计算出最多可以完成多少任务.输入描述 第一行是一个整数 N,接下来 N 行每行两个整数 T1,T2 描述一个任务:完成这个任务需要 T1 秒,如果在 T2 秒之内还没有完成任务,这个任务就到截稿时间了.输出描