Codeforces 671D. Roads in Yusland 题解

题目链接:D. Roads in Yusland

题目大意:洛谷


题解:这题目前会的有两种做法。

第一种比较简单,设(f[i][j])为到了第(i)个点,链上覆盖到深度为(j)是的最小代价,然后写出转移方程,是(f[i][j]=sum {f[son][k]}(max k=j)),然后发现可以线段树合并,所以直接写就可以了,注意一下需要回收空间。

第二种是设(f[i])表示覆盖了(i)的子树以及它的父边的最小代价,这个 DP 有一些麻烦,因为我们的信息有一些少,考虑如何转移,对于(xin son(y)),将(x)中的信息转移到(y)上,对于一个权值为(k)的方案,接下来要分情况讨论。

  • 如果向上延伸超过了(y),那么(y)的权值应为(k+sum_{zin son(y)}f[z]- f[x])
  • 如果恰好延伸到(y),那么不合法,应当舍弃。

所以说,我们只需要维护一个小根堆即可。

时间复杂度均为(O(nlog n))

#include <vector>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Maxn=300000;
int n,m;
int root[Maxn+5];
int d[Maxn+5],tot;
ll f[Maxn+5],ans;
vector<int> edge[Maxn+5];
vector<pair<int,int> > p[Maxn+5];
struct Node{
	pair<ll,int> x;
	ll z;
	int l,r,f,d;
}t[Maxn+5];
void add(int x,ll k){
	if(x){
		t[x].x.first+=k;
		t[x].z+=k;
	}
}
void spd(int x){
	if(t[x].z){
		add(t[x].l,t[x].z);
		add(t[x].r,t[x].z);
		t[x].z=0;
	}
}
int merge(int x,int y){
	if(x==0||y==0){
		return x|y;
	}
	if(t[x].x>t[y].x){
		swap(x,y);
	}
	spd(x);
	t[t[x].r=merge(t[x].r,y)].f=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;
}
void dfs(int x,int fa){
	d[x]=d[fa]+1;
	for(pair<int,int> v:p[x]){
		t[++tot].x=v;
		t[tot].d=1;
		root[x]=merge(root[x],tot);
	}
	ll s=0;
	for(int y:edge[x]){
		if(y!=fa){
			dfs(y,x);
			s+=f[y];
			add(root[y],-f[y]);
			root[x]=merge(root[x],root[y]);
		}
	}
	add(root[x],s);
	while(root[x]&&d[t[root[x]].x.second]>=d[x]){
		spd(root[x]);
		root[x]=merge(t[root[x]].l,t[root[x]].r);
	}
	if(root[x]==0){
		puts("-1");
		exit(0);
	}
	f[x]=t[root[x]].x.first;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		edge[x].push_back(y);
		edge[y].push_back(x);
	}
	for(int i=1,x,y,z;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		p[x].push_back(make_pair(z,y));
	}
	d[1]=1;
	for(int x:edge[1]){
		dfs(x,1);
		ans+=f[x];
	}
	printf("%lld
",ans);
	return 0;
}
本博客欢迎转载,转载请注明文章出处作者
原文地址:https://www.cnblogs.com/withhope/p/13576804.html