Weight the Tree (CF2,D) (贪心+分段处理+ 树上DP(邻居问题))

 

思路:

  • 题目要 求好节点数最大时的最小值
  • 好节点数是前提条件,那么就分段考虑, 先看哪些点是好节点, 在看好节点时,如何让值最小
  • 贪心发现规律: 2个点之间不能同时好节点, 对于不是好节点的点,然他权值为1即可,好节点为儿子数
  • dp转移: dp[i][0], 无所谓儿子是好是坏, 当儿子权值一样时, 就以num[b][0],num[b][1]为标准来更新这个num[i][0],选其小的.  ------
  • p[i][1], 儿子必须全是不好
  • 注意初始化即可
  • 再来一个dfs 记录ans即可. 
#include <bits/stdc++.h>
using namespace std;
#define ri register int 
#define M 2000005

int n,m;
int T;
vector <int> p[M];
int vis[M];
int dp[M][3];
int num[M][3];
int val[M];
void dfs(int a)
{
    vis[a]=1;
    for(ri i=0;i<p[a].size();i++)
    {
        int b=p[a][i];
        if(vis[b]) continue;
        dfs(b);
        
        if(dp[b][0]>dp[b][1])
        {
            dp[a][0]+=dp[b][0];
            num[a][0]+=num[b][0];
        }
        else if(dp[b][0]<dp[b][1]) dp[a][0]+=dp[b][1],num[a][0]+=num[b][1];
        else{
            dp[a][0]+=dp[b][0];
            num[a][0]+=min(num[b][0],num[b][1]);
        }
        num[a][1]+=num[b][0];    
        dp[a][1]+=dp[b][0];
    }
    num[a][0]++;
    num[a][1]+=p[a].size();    
    dp[a][1]++;
}
long long ans=0;
void dfs2(int a,int t)
{
    vis[a]=1;
    if(t==0)
    {
        ans++;
        val[a]=1;
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(vis[b]) continue;
            if(dp[b][0]>dp[b][1])
            {
                dfs2(b,0);
            }
            else if(dp[b][0]<dp[b][1]) dfs2(b,1);
            else{
                if(num[b][0]<=num[b][1]) dfs2(b,0);
                else dfs2(b,1);
            }
        }
    }
    else
    {
        ans+=p[a].size();
        val[a]=p[a].size();
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i];
            if(vis[b]) continue;
            dfs2(b,0);
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    
    cin>>n;

    for(ri i=1;i<n;i++)
    {
        int a,b;
        cin>>a>>b;
        p[a].push_back(b);
        p[b].push_back(a);
    }    
    if(n==2)
    {
        cout<<"2 2\n";
        cout<<"1 1";
        return 0;    
    }
    dfs(1);
    memset(vis,0,sizeof(vis));
            if(dp[1][0]>dp[1][1])
            {
                dfs2(1,0);
            }
            else if(dp[1][0]<dp[1][1]) dfs2(1,1);
            else{
                if(num[1][0]<=num[1][1]) dfs2(1,0);
                else dfs2(1,1);
            }
    cout<<max(dp[1][0],dp[1][1])<<" "<<ans<<"\n";
    for(ri i=1;i<=n;i++) cout<<val[i]<<" ";
    
    
}
View Code

后记:

  • 树上dp,关键是这个dp, 和 树上贡献的题 相互区分一下, 是一个包含关系. DP可以处理很多问题
  • 有前提条件就分段做

 

posted @ 2022-11-08 11:23  VxiaohuanV  阅读(14)  评论(0编辑  收藏  举报