题目链接

题目要求我们求出选出若干条路径并最小化花费,如果这是在链上,我们可以考虑直接枚举每条路径的右端点 dp,那树呢?把路径剖分整个覆盖的集合就不一定连续了,没法 dp,况且题目里给了很强的条件:路径一定是从孩子到祖先,硬转链用不上这个性质,貌似不太对。

上述思考启发我们利用树的形态设计算法,而利用节点的子树分割成子问题是一个通常的思考方向,我们从此处入手,考虑如果要覆盖一棵树需要什么条件,首先,根节点的每棵子树必须被覆盖,并且还要有一条能向外延伸的边以覆盖连接根节点和子树根节点的边。

考虑设计 dp 以维护上述条件,为了维护前者,我们可以钦定每个节点的状态所选择的解中子树全部被覆盖;而后者,由于子树内所有节点的祖先并都在固定的一条链上,我们可以添加一维确定向外延伸的长度。具体地,我们设 \(f_{u,j}\) 表示完全覆盖节点 \(u\) 的子树且向外延伸了 \(j\) 个长度的最小花费。

转移是显然的,设 \(g_u=\min f_{u,j}\),则 \(f_{u,j}=\min \{f_{v,j}\}+\sum_{k\in son(v)} g_k -g_v\)。这个做法是 \(O(n^2)\) 的。

由于转移比较简单,我们考虑能不能用数据结构维护第二维,需要支持合并取 min 和全局加,用线段树合并就可以维护,时间和空间复杂度都是 \(O((n+m)\log n)\) 的。

但是本题空间复杂度限制比较紧,注意到线段树中有很多没有用的节点,并且不能动态调整空间。考虑换成平衡树,每个节点储存二元组 \((j,cost)\) 表示向上延伸 \(j\) 距离花费 \(cost\),全局加可以直接打标记,合并可以直接启发式合并,全部插到里面就行,注意为了保证复杂度,如果有向上延伸相同距离的状态我们只能保留一个。

问题是取 min,由于刚才我们已经要求平衡树按第一维排序,我们不好直接求全局 min,但是注意到如果一个状态相比于另一个能覆盖更前面的边但是花费也更小,我们不必保留另一个状态,因此在满足一维排好序的情况下另一位也排好了。平衡树可以用 set,时间复杂度 \(O((n+m)\log^2 n)\),但是空间复杂度变成了线性,可以通过。

// Problem: Roads in Yusland
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF671D
// Memory Limit: 250 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org) #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
#define ll long long
#define N 300005
using namespace std;
ll read(){
ll x=0;char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x;
}
ll e,head[N<<1],nex[N<<1],to[N<<1],dep[N],tag[N],rt[N];
struct Node{
ll num,val;
bool operator <(const Node &x)const{
return num>x.num;
}
};
vector<Node> E[N];
set<Node> t[N];
void add(ll u,ll v){
to[++e]=v;nex[e]=head[u];head[u]=e;
to[++e]=u;nex[e]=head[v];head[v]=e;
}
ll flag;
void insert(ll u,ll num,ll val){
if(t[u].size()==0){t[u].insert((Node){num,val});return;}
auto it=t[u].lower_bound((Node){num,val});
if(it==t[u].end() || (it->num)!=num){
if(it!=t[u].end() && it->val<=val)return;
if(it==t[u].begin()){t[u].insert((Node){num,val});return;}
auto itr=it,itl=it;
while(itl!=t[u].begin()){
itl--;if(itl->val<val){itl++;break;}
}
if(itl!=itr)t[u].erase(itl,itr);
t[u].insert((Node){num,val});return;
}
if(it->val<=val) return;
it++;auto itr=it;it--;
while(it!=t[u].begin()){
it--;if(it->val<val){it++;break;}
}
t[u].erase(it,itr);
t[u].insert((Node){num,val});
}
void dfs(ll u,ll fa){
rt[u]=u;
ll tot=0;dep[u]=dep[fa]+1;
for(ll i=head[u];i;i=nex[i]){
ll v=to[i];if(v==fa) continue;
dfs(v,u);if(flag) return;
if(t[rt[v]].size()==0){flag=1;return;}
if((t[rt[v]].begin()->num)>dep[u])t[rt[v]].erase(t[rt[v]].begin());
if(t[rt[v]].size()==0){flag=1;return;}
tot+=(t[rt[v]].begin()->val+tag[v]);tag[v]=-(t[rt[v]].begin()->val);
}
ll tmp=300001;
for(ll i=head[u];i;i=nex[i]){
ll v=to[i];if(v==fa) continue;
if(t[rt[v]].size()>t[rt[u]].size()){tmp=v;rt[u]=rt[v];tag[u]=tot+tag[v];}
}
for(ll i=head[u];i;i=nex[i]){
ll v=to[i];if(v==fa || rt[v]==rt[u]) continue;
for(auto it=t[rt[v]].begin();it!=t[rt[v]].end();it++){
insert(rt[u],it->num,it->val+tag[v]-tag[tmp]);
}
}
for(ll i=0;i<E[u].size();i++){
insert(rt[u],dep[E[u][i].num],E[u][i].val-tag[tmp]);
}
}
int main(){
ll n,m,u,v,w;n=read();m=read();
for(ll i=1;i<n;i++){u=read();v=read();add(u,v);}
for(ll i=1;i<=m;i++){
u=read();v=read();w=read();E[u].push_back((Node){v,w});
}
if(n==1){cout<<0;return 0;}
dfs(1,0);
if(flag) cout<<-1;
else cout<<(t[rt[1]].begin()->val)+tag[1];
return 0;
}

CF671D Roads in Yusland 题解的更多相关文章

  1. CF671D Roads in Yusland

    一道很玄妙的题= = 我们考虑先考虑DP 那么有$f[x]=min(c+\sum f[y])$ $f[x]$表示覆盖x的子树和x->fa[x]的所有边最小代价 我们枚举一条边c覆盖的x-> ...

  2. 【CF671D】Roads in Yusland(贪心,左偏树)

    [CF671D]Roads in Yusland(贪心,左偏树) 题面 洛谷 CF 题解 无解的情况随便怎么搞搞提前处理掉. 通过严密(大雾)地推导后,发现问题可以转化成这个问题: 给定一棵树,每条边 ...

  3. Codeforces 671 D. Roads in Yusland

    题目描述 Mayor of Yusland just won the lottery and decided to spent money on something good for town. Fo ...

  4. [Codeforces671D]Roads in Yusland

    [Codeforces671D]Roads in Yusland Tags:题解 题意 luogu 给定以1为根的一棵树,有\(m\)条直上直下的有代价的链,求选一些链把所有边覆盖的最小代价.若无解输 ...

  5. 【CF617D】Roads in Yusland

    [CF617D]Roads in Yusland 题面 蒯的洛谷的 题解 我们现在已经转化好了题目了,戳这里 那么我们考虑怎么求这个东西,我们先判断一下是否所有的边都能被覆盖,不行的话输出\(-1\) ...

  6. 【CodeForces】671 D. Roads in Yusland

    [题目]D. Roads in Yusland [题意]给定n个点的树,m条从下往上的链,每条链代价ci,求最少代价使得链覆盖所有边.n,m<=3*10^5,ci<=10^9,time=4 ...

  7. codesforces 671D Roads in Yusland

    Mayor of Yusland just won the lottery and decided to spent money on something good for town. For exa ...

  8. 题解-Codeforces671D Roads in Yusland

    Problem Codeforces-671D 题意概要:给定一棵 \(n\) 点有根树与 \(m\) 条链,链有费用,保证链端点之间为祖先关系,问至少花费多少费用才能覆盖整棵树(\(n-1\) 条边 ...

  9. CF671D:Roads in Yusland

    n<=300000个点的树,给m<=300000条带权路径(ui,vi,保证vi是ui的祖先)求覆盖整棵树每条边的最小权和. 好题好姿势!直观的看到可以树形DP,f[i]表示把点i包括它爸 ...

  10. 【CF671D】 Roads in Yusland(对偶问题,左偏树)

    传送门 洛谷翻译 CodeForces Solution emmm,先引入一个对偶问题的概念 \(max(c^Tx|Ax \leq b)=min(b^Ty|A^Ty \ge c)\) 考虑这个式子的现 ...

随机推荐

  1. CF961E Tufurama题解

    我们维护一个存储下标数据的树状数组,先将 \(1\sim n\) 插入树状数组. 用 \(a\) 表示原数组,\(b\) 表示按照 \(a_i\) 排序后的数组. 我们从 \(1\) 开始统计,直到 ...

  2. 编码技巧 --- 使用dynamic简化反射

    引言 dynamic 是 Framework 4.0 就出现特性,它的出现让 C# 具有了弱语言类型的特性.编译器在编译的时候不再对类型进行检查,默认 dynamic 对象支持开发者想要的任何特性. ...

  3. UE构建基础和实践:四、使用脚本打包

    序言 使用UE版本为UE5.20 在实际项目中,我们常常使用自动化脚本来进行构建,它可以增加我们的生产效率,并降低人为操作带来的失误风险. BuildGraph BuildGraph UE官方提供的构 ...

  4. 【go语言】1.2.1 Go 环境安装

    Go 语言的安装过程非常简单,无论你使用的是哪种操作系统,都可以按照下面的步骤来进行. Windows 系统 前往 Go 语言的官方下载页面:https://golang.org/dl/ 根据你的操作 ...

  5. 最全面的JAVA多线程知识总结

    ​ 背景: 2023年经营惨淡,经历了裁员就业跳槽再就业,在找工作过程中对于知识的梳理和总结,本文总结JAVA多线程. 应用场景: 需要同时执行多个任务或处理大量并发请求时, 目前常用的场景有: We ...

  6. python中将时间转换为时间戳

    某平台url中的时间格式为时间戳,将时间变量传入url前,需要将固定格式的时间转换为时间戳.使用python中的time模块,对时间的几种格式进行转换. strptime(),将时间字符串转换成 结构 ...

  7. [ABC138F] Coincidence

    2023-02-03 题目 题目传送门 翻译 翻译 难度&重要性(1~10):6 题目来源 AtCoder 题目算法 数位dp 解题思路 \(1.\) 当 \(2x\leq y\),有\(y- ...

  8. 【LaTeX】语法(更新中)

    目录 长度 空行 空格 超链接 数学公式 段落中(隐式) 单独成段(显式) 居中,左对齐,右对齐 居中 左对齐 右对齐 参考文献配置 TODO 参考资料 中文支持参考环境配置中的 内容,在这里不做重复 ...

  9. 1Nginx基础及编译安装

    1Nginx基础 1.Nginx概述 Nginx 功能介绍 Nginx(发音为"engine-x")是一个开源的高性能 HTTP和反向代理服务器.它具有以下功能: 1.静态文件服务 ...

  10. Python API接口对接详解与实践

    ​ 在数字化时代,数据交互已经成为各种应用的必备功能.API(应用程序编程接口)就是实现不同应用程序之间数据交互的一种方式.Python作为一种功能强大的编程语言,也提供了许多用于对接API的库和框架 ...