zoukankan      html  css  js  c++  java
  • 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;
    }
    
    本博客欢迎转载,转载请注明文章出处作者
  • 相关阅读:
    linux kgdb 补丁
    linux kdb 内核调试器
    linux 使用 gdb
    linux 系统挂起
    linux oops 消息
    linux strace 命令
    linux ioctl 方法
    linux seq_file 接口
    [数据结构] 迷宫问题(栈和队列,深搜和广搜)
    简化浏览器地址栏訪问路径
  • 原文地址:https://www.cnblogs.com/withhope/p/13576804.html
Copyright © 2011-2022 走看看