n<=300000个点的树,给m<=300000条带权路径(ui,vi,保证vi是ui的祖先)求覆盖整棵树每条边的最小权和。

好题好姿势!直观的看到可以树形DP,f[i]表示把点i包括它爸爸下面那条边都覆盖的最小权,就用经过他爸爸那条边的所有路径,各条路径加上一些子树信息来更新即可。

这样时间炸,那看看怎么优化。实际上我们不是在单纯地用一条路径更新答案,而是这样一个东西:

其中红色那条是题目给的路径,实际上是加上蓝色边连接的点的f[i]来更新边2上端的那个点的答案的。也就是说,一条路径来更新答案,要在这条路径的尾部加上那些点权(1),然后在更新某个点的答案的时候加上这个点下面的这条路径的分叉(2)。而更新一个点的路径,其实都是这个点对应子树的路径。至于子树中那些够不到这个点的路径,只需在扫到头的时候把这条路径的答案变inf即可。

为了实现这个操作,即找到“起点在子树里的所有路径的答案”,我们用dfs序给每个路径的起点(下端点)编号,再dfs求每个点的答案;每次求答案时,先把以该点为起点的新路径赋初值,即该点所有子树的f[j]和,并把终点在该点的路径答案置inf;然后给经过该点的路径加“分叉”;由于dfs序编号好了,能更新这个节点的路径(上面提到的起点在这个子树内的路径)是连续的一个区间,因此用个线段树维护区间min即可。

至于加“分叉”,观察可以发现:假如经过i的某路径来自子树j,那么应该把它答案加上点i其他儿子的f和。所以在加“分叉”时只需要再枚举一次孩子,把孩子子树内所有的路径加上其他孩子的f[j]和即可。

废话少说见代码!

 #include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
//#include<iostream>
using namespace std; int n,m;
#define maxn 300011
struct Edge{int to,next;}edge[maxn*];int first[maxn],start[maxn],end[maxn],le=;
void in(int x,int y,int* first) {edge[le].to=y;edge[le].next=first[x];first[x]=le++;}
void insert(int x,int y,int* first) {in(x,y,first);in(y,x,first);}
int dfn[maxn],Left[maxn],Right[maxn],Time=;
void dfs(int x,int fa)
{
Left[x]=Time+;
for (int i=start[x];i;i=edge[i].next)
{
Edge &e=edge[i];
dfn[e.to]=++Time;
}
for (int i=first[x];i;i=edge[i].next)
{
Edge &e=edge[i];
if (e.to!=fa) dfs(e.to,x);
}
Right[x]=Time;
}
#define LL long long
LL val[maxn];
const LL inf=1e15+;
struct SMT
{
struct Node
{
LL Min;
LL add;
int l,r;
int ls,rs;
}a[maxn*];
int size;
SMT() {size=;}
void build(int &x,int L,int R)
{
x=++size;
a[x].Min=inf;a[x].add=;
a[x].l=L;a[x].r=R;
if (L==R) {a[x].ls=a[x].rs=;}
else
{
const int mid=(L+R)>>;
build(a[x].ls,L,mid);
build(a[x].rs,mid+,R);
}
}
void build() {int x;build(x,,m);}
void up(int x)
{
const int &p=a[x].ls,&q=a[x].rs;
a[x].Min=min(a[p].Min,a[q].Min);
}
void addsingle(int x,LL v)
{
a[x].Min+=v;
a[x].Min=min(inf,a[x].Min);
a[x].add+=v;
}
void down(int x)
{
const int &p=a[x].ls,&q=a[x].rs;
if (a[x].add)
{
addsingle(p,a[x].add);
addsingle(q,a[x].add);
a[x].add=;
}
}
int ql,qr;LL v;
void be(int x)
{
if (a[x].l==a[x].r) {a[x].Min=v;return;}
down(x);
const int mid=(a[x].l+a[x].r)>>;
if (ql<=mid) be(a[x].ls);
if (ql> mid) be(a[x].rs);
up(x);
}
void be(int p,LL v) {ql=qr=p;this->v=v;be();}
void Add(int x)
{
if (ql<=a[x].l && a[x].r<=qr) {addsingle(x,v);return;}
down(x);
const int mid=(a[x].l+a[x].r)>>;
if (ql<=mid) Add(a[x].ls);
if (qr> mid) Add(a[x].rs);
up(x);
}
void Add(int L,int R,LL v) {if (L>R) return;ql=L;qr=R;this->v=v;Add();}
LL query(int x)
{
if (ql<=a[x].l && a[x].r<=qr) return a[x].Min;
down(x);
const int mid=(a[x].l+a[x].r)>>;LL ans=inf;
if (ql<=mid) ans=min(ans,query(a[x].ls));
if (qr> mid) ans=min(ans,query(a[x].rs));
return ans;
}
LL query(int L,int R) {if (L>R) return inf;ql=L;qr=R;return query();}
}t;
LL f[maxn];
void play(int x,int fa)
{
LL tot=;
for (int i=first[x];i;i=edge[i].next)
{
Edge &e=edge[i];if (e.to==fa) continue;
play(e.to,x);
tot=min(inf,f[e.to]+tot);
}
for (int i=start[x];i;i=edge[i].next)
{
Edge &e=edge[i];
t.be(dfn[e.to],tot+val[e.to]);
}
for (int i=end[x];i;i=edge[i].next)
{
Edge &e=edge[i];
t.be(dfn[e.to],inf);
}
if (x==) f[]=tot;
else if (tot<inf)
{
for (int i=first[x];i;i=edge[i].next)
{
Edge &e=edge[i];if (e.to==fa) continue;
t.Add(Left[e.to],Right[e.to],tot-f[e.to]);
}
f[x]=t.query(Left[x],Right[x]);
}
else f[x]=inf;
}
void play()
{
dfs(,);
t.build();
play(,);
}
int x,y;LL v;
int main()
{
scanf("%d%d",&n,&m);
for (int i=;i<n;i++)
{
scanf("%d%d",&x,&y);
insert(x,y,first);
}
for (int i=;i<=m;i++)
{
scanf("%d%d%I64d",&x,&y,&val[i]);
in(x,i,start);
in(y,i,end);
}
play();
printf(f[]>=inf?"-1\n":"%I64d\n",f[]);
return ;
}

还有一种贪心写法哦!

CF671D:Roads in Yusland的更多相关文章

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

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

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

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

  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. CF671D Roads in Yusland

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

  9. 题解-Codeforces671D Roads in Yusland

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

随机推荐

  1. ceph脚本-自动部署计算机节点

    依然还在加班中,最近确实忙的脚打后脑勺! 又花了些时间丰富ceph脚本,可以连带着自动部署计算机节点了. 这一部分内容是后加的.可以关注我的公众号获取更多的项目代码和讲解!波神与你同行哦,加油!!!

  2. android 防止bitmap 内存溢出

    在android开发过程中经常会处理网络图片发送内存溢出,那么怎么解决这种问题? 思路: 下载到本地 通过网络获取和文件下载存放到手机中目录 代码: // 获取网络 public InputStrea ...

  3. Bloom Filter概念和原理【转】

    Bloom Filter概念和原理 Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合.Bloom Filter的这种高效是有 ...

  4. CENTOS6.4上KVM虚拟机环境搭建

    CENTOS6.4上KVM虚拟机环境搭建   关键词: KVM,虚拟机,windows7, VNC, 桥接网络,br0, SCSI, IDE   环境: host: CENTOS6.4 guest: ...

  5. LibreOJ #109. 并查集

    题目描述 这是一道模板题. 维护一个 nnn 点的无向图,支持: 加入一条连接 uuu 和 vvv 的无向边 查询 uuu 和 vvv 的连通性 由于本题数据较大,因此输出的时候采用特殊的输出方式:用 ...

  6. 利用Jenkins打包ISO和QCOW2镜像文件

    现在的云虚拟化环境越来越多,经常会碰到需要修改并重新打包新的ISO或QCOW2镜像文件.通过手工的方式会比较麻烦,所以在镜像发布的生产环境中可以利用Jenkins来进行定期打包发布,以下介绍Jenki ...

  7. 微信小程序开发系列五:微信小程序中如何响应用户输入事件

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...

  8. flask 项目部分业务逻辑

    @passport_blu.route('/image_code') def get_image_code(): """ 生成图片验证码并返回 1. 取到参数 2. 判断 ...

  9. VC无窗口控制台程序

    VC无窗口控制台程序 #pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartu ...

  10. 暑假集训 || bitset

    bitset是一个存储0和1的数组 可以快速的把两个bitset的每一位对应做与或啥的 在可以用01串表示某个状态的时候可以应用到它 就是有两个集合,求它们的交集 bitset <> a, ...