zoukankan      html  css  js  c++  java
  • CF1088E Ehab and a component choosing problem(树形dp)

    传送门


    解题思路

    一开始想到的是类似0/1分数规划的方法进行二分。

    后来发现答案可以贪心解决。

    我们可以发现最后的答案一定是由若干权值和相同的集合组成的,因为若其中一个集合的点权和较大,则将其单独拿出来显然更优。

    所以可以先dfs一遍用树形dp求出最大的集合的大小ans,再dfs一遍,若扫到某个集合的点权和等于ans,则k++,且删掉这个子树(令dp[u]变为0)。

    最后答案就是ans*k和k。

    AC代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn=3e5+5;
    int n,p[maxn],cnt;
    long long a[maxn],dp[maxn],ans=-1e18,k;
    struct node{
    	int v,next;
    }e[maxn*2];
    void insert(int u,int v){
    	cnt++;
    	e[cnt].v=v;
    	e[cnt].next=p[u];
    	p[u]=cnt;
    }
    void dfs1(int u,int fa){
    	for(int i=p[u];i!=-1;i=e[i].next){
    		int v=e[i].v;
    		if(v==fa) continue;
    		dfs1(v,u);
    		if(dp[v]>0) dp[u]+=dp[v];
    	}
    	dp[u]+=a[u];
    	ans=max(ans,dp[u]);
    }
    void dfs2(int u,int fa){
    	for(int i=p[u];i!=-1;i=e[i].next){
    		int v=e[i].v;
    		if(v==fa) continue;
    		dfs2(v,u);
    		if(dp[v]>0) dp[u]+=dp[v];
    	}
    	dp[u]+=a[u];
    	if(dp[u]==ans) k++,dp[u]=0;
    }
    int main(){
    	ios::sync_with_stdio(false);
    	memset(p,-1,sizeof(p));
    	cin>>n;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<n;i++){
    		int u,v;
    		cin>>u>>v;
    		insert(u,v);
    		insert(v,u);
    	}
    	dfs1(1,0);
    	memset(dp,0,sizeof(dp));
    	dfs2(1,0);
    	cout<<ans*k<<" "<<k<<endl;
    	return 0;
    }
    
  • 相关阅读:
    51nod1376 最长递增子序列的数量
    51nod1201 整数划分
    51nod1202 子序列个数
    51nod 博弈论水题
    51nod1052 最大M子段和
    51nod1678 lyk与gcd
    51nod1262 扔球
    BZOJ2763, 最短路
    吃西瓜 最大子矩阵 三维的。 rqnoj93
    noip2015 信息传递 强连通块
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/15386179.html
Copyright © 2011-2022 走看看