Codeforces 671 D. Roads in Yusland

时间:2023-02-03 17:02:57

题目描述

Mayor of Yusland just won the lottery and decided to spent money on something good for town. For example, repair all the roads in the town.

Yusland consists of n intersections connected by n - 1 bidirectional roads. One can travel from any intersection to any other intersection using only these roads.

There is only one road repairing company in town, named "RC company". Company's center is located at the intersection 1. RC company doesn't repair roads you tell them. Instead, they have workers at some intersections, who can repair only some specific paths. The i-th worker can be paid ci coins and then he repairs all roads on a path from ui to some vi that lies on the path from ui to intersection 1.

Mayor asks you to choose the cheapest way to hire some subset of workers in order to repair all the roads in Yusland. It's allowed that some roads will be repaired more than once.

If it's impossible to repair all roads print  - 1.

题目大意:

每一条边都需要被修建一次,雇佣第\(i\)个工人需要花 \(ci\) 的钱,能够帮你修建 \(ui\),\(vi\),之间的所有道路,并且保证 \(vi\) 是,\(ui\) 到根的路径上的点

解题报告:

用时:2h,6MLE

分析这题,叶子节点必须选,然后对于没有被覆盖的也必须从子树中选,但是需要决策,所以只能DP,考虑状态:

很容易想到最暴力的DP,定义 \(f[x][j]\),表示 \(x\) 子树内的边都被修过,且已选工人最远能覆盖到的深度为 \(j\),转移非常简单,但是复杂度不对,考虑消除第二维:不会.....

容易想到树链剖分,即把当前节点能更新到的父节点做一遍链剖,更新线段树

显然不行,因为不能保证其他子树都被覆盖

参考题解:

思想非常巧妙,大概是把其他子树中的DP值累加到某一棵子树上,这样就巧妙的使得线段树中的DP值都是合法的了,为了消除在 \(vi\) 结束的影响,我们在 \(ui\) 和 \(vi\) 上分别挂链,然后dfs时对应线段树中添加删除即可

注意实现时可以考虑不参考我的方法,其实不一定只对关键点建立线段树,不然细节很多

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=300005;const ll inf=2e15;
int head[N],nxt[N<<2],to[N<<2],num=0,n,m,val[N],dfn[N],DFN=0,L[N],R[N],s[N],t[N];
void link(int x,int y,int *h){nxt[++num]=h[x];to[num]=y;h[x]=num;}
void priwork(int x,int last){
L[x]=DFN+1;
for(int i=s[x];i;i=nxt[i])dfn[to[i]]=++DFN;
for(int i=head[x];i;i=nxt[i])
if(to[i]!=last)priwork(to[i],x);
R[x]=DFN;
}
ll tree[N<<2],mark[N<<2],f[N];
#define ls (node<<1)
#define rs (node<<1|1)
void build(int l,int r,int node){
tree[node]=inf;if(l==r)return ;
int mid=(l+r)>>1;
build(l,mid,ls);build(mid+1,r,rs);
tree[node]=Min(tree[ls],tree[rs]);
}
il void updata(int l,int r,int node,int sa,int se,ll to){
if(sa>se)return ;
if(l>se || r<sa)return ;
if(sa<=l && r<=se){
tree[node]=Min(tree[node]+to,inf);
mark[node]=Min(mark[node]+to,inf);
return ;
}
int mid=(l+r)>>1;
updata(l,mid,ls,sa,se,to);updata(mid+1,r,rs,sa,se,to);
tree[node]=Min(tree[ls],tree[rs])+mark[node];
}
il void Modify(int l,int r,int node,int sa,ll to){
if(l==r){tree[node]=Min(to,inf);return ;}
int mid=(l+r)>>1;
if(sa<=mid)Modify(l,mid,ls,sa,to);
else Modify(mid+1,r,rs,sa,to);
tree[node]=Min(tree[ls],tree[rs])+mark[node];
}
il ll query(int l,int r,int node,int sa,int se){
if(sa>se)return inf;
if(l==sa && r==se)return tree[node];
int mid=(l+r)>>1;
if(sa>mid)return query(mid+1,r,rs,sa,se)+mark[node];
else if(se<=mid)return query(l,mid,ls,sa,se)+mark[node];
return min(query(l,mid,ls,sa,mid),query(mid+1,r,rs,mid+1,se))
+mark[node];
}
il void dfs(int x,int last){
int u;ll tot=0;
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(to[i]==last)continue;
dfs(u,x);
tot+=f[u];tot=Min(tot,inf);
}
if(x==1){f[x]=tot;return ;}
for(int i=s[x];i;i=nxt[i])Modify(1,m,1,dfn[to[i]],tot+val[to[i]]);
for(int i=t[x];i;i=nxt[i])Modify(1,m,1,dfn[to[i]],inf);
for(int i=head[x];i;i=nxt[i]){
u=to[i];if(u==last)continue;
updata(1,m,1,L[u],R[u],tot-f[u]);
}
f[x]=query(1,m,1,L[x],R[x]);
}
void work()
{
int x,y;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
link(x,y,head);link(y,x,head);
}
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&val[i]);
link(x,i,s);link(y,i,t);
}
priwork(1,1);build(1,m,1);dfs(1,1);
if(f[1]!=inf)printf("%lld\n",f[1]);
else puts("-1");
} int main(){work();return 0;}