题目链接

题目要求我们求出选出若干条路径并最小化花费,如果这是在链上,我们可以考虑直接枚举每条路径的右端点 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. 【Kubernetes】kubectl常用命令手册 -- 持续更新

    元信息 api列表: kubectl api-resources kubectl api-resources -o wide 用法查询: kubectl explain <>.<&g ...

  2. 因为一条DDL,差点搞挂整个系统,这次真的长了教训

    有一次在线上提了一个sql变更,就是下面这条, -- 修改字段的数据类型由varchar(500)变更为text ALTER TABLE t MODIFY COLUMN name text; 提完之后 ...

  3. 刷了一个月AI歌唱的视频 做一个大胆预测

    现在的AI热点转到ChatAI和AI唱歌去了 很好理解(现在每天在看Neuro的切片 感慨这才是看V的初心 可惜Neuro这个形象在创立的时候只是一个ChatAI 和游戏用的GameBOT并不是同一个 ...

  4. 正确处理 CSV 文件的引号和逗号

    CSV(Comma-Separated Values,逗号分割值),就是用纯文本的形式存储表格数据,最大的特点就是方便. 作为开发,我们经常面临导数据的问题,特别是后台系统,产品或者运营的同事常常会提 ...

  5. 解决Oracle jdbc驱动包maven下载失败问题

    由于Oracle版权限制,其jdbc驱动包不让人随便下载,这就给maven的下载和编译带来了麻烦. 解决办法是先获取jar包(方法一:去oracle官网下载,方法二:去oracle安装目录如produ ...

  6. [NOIP2011 提高组] 聪明的质监员【题解】

    题目 小 T 是一名质量监督员,最近负责检验一批矿产的质量.这批矿产共有 \(n\) 个矿石,从 \(1\) 到 \(n\) 逐一编号,每个矿石都有自己的重量 \(w_i\) 以及价值 \(v_i\) ...

  7. C语言基础--逻辑判断和循环

    目录 一.储存标识符 1.auto 2.register 3.static 4.const 二.运算符 1.逻辑运算符 2.位运算符 3.运算符 4.三元运算符 三.选择结构 1.if判断 1.1 i ...

  8. vim玩法 .vimrc配置映射指令nnoremap、inoremap

    编辑 vimrc 文件, vi ~/.vimrc vim中的映射指令,用于将一个按键绑定到某一个操作上. map: 执行映射指令,执行时会进行递归替换,可能会出现"按键循环"的情况 ...

  9. 【算法】单调栈 & 单调队列学习笔记

    1. 单调栈简介 1.1 前言 今天是 2023/1/15,一中寒假集训阶段性的结束了.集训的学习笔记可以在本人 blogs 的[算法]标签栏中找. 马上就要过年了,提前祝大家新年快乐! 1.2 什么 ...

  10. 【技术积累】Linux中的命令行【理论篇】【八】

    basename命令 命令介绍 在Linux中,basename命令用于从给定的路径中提取文件名或目录名.它的语法如下: basename [选项] [路径] 命令介绍 选项:-s, --suffix ...