@codechef - KILLER@ Painting Tree
@description@
给定一个 N 个点的有根树,标号 1 到 N,以 1 为根。定义一个结点 x 的深度 dep[x] 为 x 到根经过的边的数量。
将整棵树剖分成若干竖直的链(链上结点总是由祖先到后代),每条竖直的链的代价如下:
\]
其中 u 是链的最低点,w 是链上的点,h 为整棵树最深的点的深度,C, H 为给定的数组。
求整棵树的一个剖分方案,使得链的代价和最小。输出最小代价和。
@solution@
考虑把链的代价函数化简,得到较为简洁的表示。
注意到 dep[w] 是一个,所以求和部分实际上可以写成 C[u] 乘上等差数列求和。
设 x 为最高点,则最终一条链的代价可以记作 \(a(u) \times dep^2[x] + b(u) \times dep[x] + c(u)\)。
其中 \(a(u) = C[u], b[u] = C[u]^2 + C[u]\),剩下 c(u) 太长了就不写了。
可以作一个 O(n^2) 的 dp,对于每个 x 枚举它所在链的最低点是哪个点,记作 u。
然后转移时除了自己这条链的代价,还要加上删去自己这条链过后其余子树的 dp 值之和。
注意到我们可以使用线段树合并快速维护 “删去某个点 u 到根 x 这条链过后其余子树的 dp 值之和”。
感觉 dp 推不动了?试试决策单调性。
注意到该二次函数的对称轴 \(x = -\frac{C[u]^2 + C[u]}{2C[u]} = -\frac{C[u] + 1}{2}\) 始终 < 0,而 dep[x] 始终 >= 0。
这意味着其实二次函数并不是完整的二次函数,只是二次函数的右半部分。但还是不好维护。
注意我们维护一次函数(斜率优化)时,除了传统的凸包维护以外,还可以用神奇的李超线段树维护。
这道题其实也可以用李超线段树,因为它满足李超线段树所要求的单调性(不好描述。。。反正正确性挺显然的)。
所以直接上李超线段树的树上线段树合并即可。
@accepted code@
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 100000;
const ll INF = (1LL << 60);
struct edge{
edge *nxt; int to;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
p = (++ecnt);
p->to = u, p->nxt = adj[v], adj[v] = p;
}
int dep[MAXN + 5], mxd, N;
ll C[MAXN + 5], H[MAXN + 5];
void init() {
ecnt = edges;
for(int i=1;i<=N;i++)
adj[i] = NULL;
}
ll dp[MAXN + 5], s[MAXN + 5];
ll a(int u) {return C[u];}
ll b(int u) {return 2*C[u]*C[u] + C[u];}
ll c(int u) {return C[u]*(C[u] + dep[u] + C[u])*(1 - dep[u]) - 2*H[u];}
struct func{
ll a, b, c;
func(ll _a=0, ll _b=0, ll _c=0) : a(_a), b(_b), c(_c) {}
ll get(ll x) {return (a*x + b)*x + c;}
};
struct segtree{
struct node{
func f; ll tg;
node *ch[2];
}pl[20*MAXN + 5], *ncnt, *NIL;
void clear() {
NIL = ncnt = pl;
NIL->ch[0] = NIL->ch[1] = NIL;
}
void maintain(node *x, ll k) {
x->tg += k, x->f.c += k;
}
void pushdown(node *x) {
if( x->tg ) {
if( x->ch[0] != NIL ) maintain(x->ch[0], x->tg);
if( x->ch[1] != NIL ) maintain(x->ch[1], x->tg);
x->tg = 0;
}
}
node *newnode(func f) {
node *p = (++ncnt);
p->ch[0] = p->ch[1] = NIL;
p->f = f, p->tg = 0;
return p;
}
void insert(node *&x, int l, int r, func f) {
if( x == NIL ) {
x = newnode(f);
return ;
}
pushdown(x);
if( x->f.get(l) > f.get(l) )
swap(x->f, f);
int m = (l + r) >> 1;
if( x->f.get(m) > f.get(m) )
swap(x->f, f), insert(x->ch[0], l, m, f);
else if( x->f.get(r) > f.get(r) )
insert(x->ch[1], m + 1, r, f);
}
node *merge(node *x, node *y, int l, int r) {
if( x == NIL ) return y;
if( y == NIL ) return x;
pushdown(x), pushdown(y);
int m = (l + r) >> 1;
x->ch[0] = merge(x->ch[0], y->ch[0], l, m);
x->ch[1] = merge(x->ch[1], y->ch[1], m + 1, r);
insert(x, l, r, y->f);
return x;
}
ll query(node *x, int l, int r, int p) {
if( x == NIL ) return INF;
int m = (l + r) >> 1;
pushdown(x);
if( p <= m ) return min(query(x->ch[0], l, m, p), x->f.get(p));
else return min(query(x->ch[1], m + 1, r, p), x->f.get(p));
}
}T;
segtree::node *rt[MAXN + 5];
void dfs(int x, int f) {
s[x] = 0;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
dfs(p->to, x), s[x] += dp[p->to];
}
rt[x] = T.newnode(func(a(x), b(x), c(x) + s[x]));
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
T.maintain(rt[p->to], s[x] - dp[p->to]);
rt[x] = T.merge(rt[x], rt[p->to], 0, mxd);
}
dp[x] = T.query(rt[x], 0, mxd, dep[x]);
}
void get_dep(int x, int f) {
dep[x] = dep[f] + 1;
for(edge *p=adj[x];p;p=p->nxt) {
if( p->to == f ) continue;
get_dep(p->to, x);
}
mxd = max(mxd, dep[x]);
}
void solve() {
scanf("%d", &N), init();
for(int i=1;i<=N;i++)
scanf("%lld%lld", &H[i], &C[i]);
for(int i=1;i<N;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
mxd = dep[0] = -1, get_dep(1, 0);
for(int i=1;i<=N;i++) dep[i] = mxd - dep[i];
T.clear(), dfs(1, 0);
printf("%lld\n", dp[1] / 2);
}
int main() {
int T; scanf("%d", &T);
while( T-- ) solve();
}
@details@
从这道题也可以看出,具有决策单调性的 dp 其实也可以使用李超线段树来维护。
感觉李超线段树挺好用的.jpg。平衡树维护凸包是什么啊,丢了丢了。
@codechef - KILLER@ Painting Tree的更多相关文章
- Codechef Union on Tree
Codechef Union on Tree https://www.codechef.com/problems/BTREE 简要题意: 给你一棵树,\(Q\)次询问,每次给出一个点集和每个点的\(r ...
- Codechef Observing the Tree
Home » Practice(Hard) » Observing the Tree https://www.codechef.com/problems/QUERY Observing the T ...
- 【点分树】codechef Yet Another Tree Problem
已经连咕了好几天博客了:比较经典的题目 题目大意 给出一个 N 个点的树和$K_i$, 求每个点到其他所有点距离中第 $K_i$ 小的数值. 题目分析 做法一:点分树上$\log^3$ 首先暴力做法: ...
- Codechef Chef Cuts Tree
该思博的时候就思博到底,套路的时候不能再套路的一道题 首先我们将联通块的大小平方和进行转化,发现它就等价于连通点对数,而这个可以转化为连接两点的边数(距离)和 所以我们考虑第\(i\)天时,一个点对\ ...
- ZJOI2019Round#2
乱听课记录 关于树的分治问题&杂题选讲 张哲宇 边分治 (边分不是很鸡肋吗) 例题一 题目大意:给出两颗有正负边权的树,求出两个点\(u,v\)使得两棵树中\((u,v)\)距离的和最大. ...
- BZOJ 3221: [Codechef FEB13] Obserbing the tree树上询问( 可持久化线段树 + 树链剖分 )
树链剖分+可持久化线段树....这个一眼可以看出来, 因为可持久化所以写了标记永久化(否则就是区间修改的线段树的持久化..不会), 结果就写挂了, T得飞起...和管理员拿数据调后才发现= = 做法: ...
- 【启发式搜索】Codechef March Cook-Off 2018. Maximum Tree Path
有点像计蒜之道里的 京东的物流路径 题目描述 给定一棵 N 个节点的树,每个节点有一个正整数权值.记节点 i 的权值为 Ai.考虑节点 u 和 v 之间的一条简单路径,记 dist(u, v) 为其长 ...
- Codechef March Cook-Off 2018. Maximum Tree Path
目录 题意 解析 AC_code @(Codechef March Cook-Off 2018. Maximum Tree Path) 题意 给你一颗\(n(1e5)\)个点有边权有点权的树,\(Mi ...
- [Codechef - ADITREE] Adi and the Tree - 树链剖分,线段树
[Codechef - ADITREE] Adi and the Tree Description 树上每个节点有一个灯泡,开始所有灯泡都是熄灭的.每次操作给定两个数 \(a,b\) ,将 \(a,b ...
随机推荐
- django2.x报错No module named 'django.core.urlresolvers'b
解决方法就是: from django.urls import reverse 最近从django1.9迁移到django2.0中出现一个意外的报错: 这个报错的原因在stack overflow上有 ...
- iOS CALayer之CAEmitterLayer粒子发射器的神奇效果
https://www.jianshu.com/p/c54ffd7412e7 想必以前QQ空间的点赞效果大家都知道吧,点赞之后按钮周围会有一圈爆裂的小圆点:还有微信的红包雨表情动画等,以及烟花,火焰效 ...
- Windows 常用工具 & 开发工具 & Chrome插件 & Firefox 插件 & 办公软件
常用工具 1.FastStone 8.0 2.印象笔记 3.Chrome 4.Beyond Compare 5.Everything 6.有道词典 7.文本编辑软件 EditPlus UltraEdi ...
- Spring学习:程序的耦合和解耦的思路分析
程序的耦合 耦合:程序间的依赖关系 包括: 类之间的依赖 方法间的依赖 解耦: 降低程序间的依赖关系 在实际开发中: 应该做到,编译期不依赖,运行时才依赖 解耦思路: 第一步:使用反射来创建对象,而避 ...
- Vertex Covers(高维前缀和)
Vertex Covers 时间限制: 5 Sec 内存限制: 128 MB提交: 5 解决: 3 题目描述 In graph theory, a vertex cover of a graph ...
- HYSBZ 1015/BZOJ1015 星球大战starwar
Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过 ...
- #socket #socketserver
#通过socket 实现简单的ssh#服务端 #服务端 import os import socket server = socket.socket() #server.bind(('0.0.0.0' ...
- TZ_16_Vue的idea入门
1.Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便 ...
- jQuery事件绑定的四种方法
jQuery中提供了四种绑定事件的方法,分别是bind.live.delegate.on,对应的解除监听的函数分别是unbind.die.undelegate.off: 一.on()方法(首选方法) ...
- Vue-Cli 3.0 + vue.config.js
虽然一直没有亲手搭建过vue项目,但是2.0的时候就开始自学(但并没有实践项目).然后公司最近有个人用3.0做了个项目,公司让我参与进去,我就顺便学习了一把3.0.(美滋滋) 因为电脑的环境还是之前自 ...