codeforces 671D

12 篇文章 0 订阅
3 篇文章 0 订阅

luogu链接

解法

设f[i]表示覆盖了i的子树和i的返祖边的最小代价,那么答案就是 ∑ i ∈ s o n [ 1 ] f [ i ] \sum_{i\in son[1]} f[i] ison[1]f[i],但是f[i]的方案不一定是最终的最优方案,如果某个方案可以向上延伸的更长,即使现在代价比较高,也有可能是最终更优的方案,所以我们需要维护所有有可能成为答案的方案。然后再给当前点选出一个最小的作为f。
因为要选最小值,所以考虑使用小根堆。
转移的方法:对于 x ∈ s o n [ y ] x\in son[y] xson[y],从x转移到y,对于x的小根堆里的一个方案:如果它不能延伸到y的父亲,就需要把它删了,否则这个方案(设其权值为val)会对y提供一种权值为 v a l + ∑ z ∈ s o n [ y ] f [ z ] − f [ x ] val+\sum_{z\in son[y]} f[z]-f[x] val+zson[y]f[z]f[x],延伸位置不变的方案。
然后对于需要删除的方案,不需要直接删除,而是在其成为堆顶以后再删除。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
inline int read(){
	char c=getchar();int t=0,f=1;
	while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
	while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
	return t*f;
}
int n,m;
struct edge{
	int v,p;
}e[maxn<<1];
int h[maxn],cnt;
inline void add(int a,int b){
	e[++cnt].p=h[a];
	e[cnt].v=b;
	h[a]=cnt;
	e[++cnt].p=h[b];
	e[cnt].v=a;
	h[b]=cnt;
}
typedef pair<int,int> pii;
vector<pii> g[maxn];
int dep[maxn];
ll f[maxn],ans;
struct node{
	pair<ll,int> x;
	ll z;
	int l,r,fa,d;
}t[maxn];
#define fi first
#define se second
inline void add(int x,ll k){
	if(x)t[x].x.fi+=k,t[x].z+=k;
}
inline void pushdown(int x){
	add(t[x].l,t[x].z);
	add(t[x].r,t[x].z);
	t[x].z=0;
}
inline int merge(int x,int y){
	if(!x||!y)return x|y;
	if(t[x].x>t[y].x)swap(x,y);
	if(t[x].z)
	pushdown(x);
	t[x].r=merge(t[x].r,y);
	t[t[x].r].fa=x;
	if(t[t[x].r].d>t[t[x].l].d)swap(t[x].l,t[x].r);
	t[x].d=t[t[x].r].d+1;
	return x;
}
int tot,rt[maxn];
void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	for(auto o:g[u]){tot++;
		t[tot].x=o,t[tot].d=1;rt[u]=merge(rt[u],tot);
	}
	ll s=0;
	for(int i=h[u];i;i=e[i].p){
		int v=e[i].v;
		if(v==fa)continue;
		dfs(v,u);s+=f[v];
		add(rt[v],-f[v]);rt[u]=merge(rt[u],rt[v]);
	}
	add(rt[u],s);
	while(rt[u]&&dep[t[rt[u]].x.se]>=dep[u]){
		if(t[rt[u]].z)
		pushdown(rt[u]);
		rt[u]=merge(t[rt[u]].l,t[rt[u]].r);
	}
	if(!rt[u]){puts("-1");exit(0);}
	f[u]=t[rt[u]].x.fi;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		add(x,y);
	}
	for(int i=1;i<=m;i++){
		int x=read(),y=read(),z=read();
		g[x].push_back(pii(z,y));
	}
	dep[1]=1;
	for(int i=h[1];i;i=e[i].p){
		int v=e[i].v;
		dfs(v,1);ans+=f[v];
	}
	printf("%lld\n",ans);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值