You are given a tree of nn vertices numbered from 11 to nn. A tree is a connected undirected graph without cycles.

For each i=1,2,…,ni=1,2,…,n, let wiwi be the weight of the ii-th vertex. A vertex is called good if its weight is equal to the sum of the weights of all its neighbors.

Initially, the weights of all nodes are unassigned. Assign positive integer weights to each vertex of the tree, such that the number of good vertices in the tree is maximized. If there are multiple ways to do it, you have to find one that minimizes the sum of weights of all vertices in the tree.

Input

The first line contains one integer nn (2≤n≤2⋅1052≤n≤2⋅105) — the number of vertices in the tree.

Then, n−1n−1 lines follow. Each of them contains two integers uu and vv (1≤u,v≤n1≤u,v≤n) denoting an edge between vertices uu and vv. It is guaranteed that the edges form a tree.

Output

In the first line print two integers  — the maximum number of good vertices and the minimum possible sum of weights for that maximum.

In the second line print nn integers w1,w2,…,wnw1,w2,…,wn (1≤wi≤1091≤wi≤109)  — the corresponding weight assigned to each vertex. It can be proven that there exists an optimal solution satisfying these constraints.

If there are multiple optimal solutions, you may print any.

Examples

input

4
1 2
2 3
2 4

output

3 4
1 1 1 1

input

3
1 2
1 3

output

2 3
1 1 1

input

2
1 2

output

2 2
1 1

input

9
3 4
7 6
2 1
8 3
5 6
1 8
8 6
9 6

output

6 11
1 1 1 1 1 1 1 3 1 

题意: 给出一颗树,现在需要你为每个点分配权值,当一个点权值等于其相邻点权值和,那么该点被称为good点,求在满足good点最大的前提下树上所有点权和最小的方案。

分析: 首先需要想到相邻两点不可能同时被记为good点,这样这道题目就有点像经典树形dp题——没有上司的舞会了,设状态dp[i][0/1]表示在以i为根的子树中且i点不是good点/i点是good点的最大总good点数,为了统计最终方案还需要记录dp2[i][0/1],dp2[i][0/1]表示在以i为根的子树中且i点不是good点/i点是good点的总权重最小值。

接下来考虑状态转移,如果当前点是good点那么所有子节点都不能是good点,所以dp[now][1] += dp[son][0],dp2[now][1] += dp2[son][0],如果当前点不是good点那么子结点可以是good点也可以不是good点,具体哪种情况取决于dp值和dp2值,如果dp[son][0] > dp[son][1],那么dp[now][0] += dp[son][0],dp2[now][0] += dp2[son][0],如果dp[son][0] < dp[son][1],那么dp[now][0] += dp[son][1],dp2[now][0] += dp2[son][1],如果dp[son][0] == dp[son][1],此时dp[now][0] += dp[son][0],毕竟都一样大,加哪个都一样,不过这时候dp2[now][0]就需要加小的那个了,在这种情况下如果dp2[son][0] < dp2[son][1],那么dp2[now][0] += dp2[son][0],否则dp2[now][0] += dp2[son][1]。

状态初始化就是对于叶子结点now,令dp[now][1] = 1, dp[now][0] = 0, dp2[now][1] = (点now相邻点个数), dp2[now][0] = 0,然后先dfs到树的最底层,回溯的过程中用子结点信息更新当前点信息。

求出dp数组和dp2数组后就可以开始构造方案了,这个过程其实和状态转移的思想类似。设一个布尔数组st[i][0/1],st[i][0]表示在根节点不是good点的情况下i号点是否为good点,st[i][1]表示在根节点是good点的情况下i号点是否为good点。根据根节点是否为good点分为两种情况,这两种情况下的最优方案都需要求解。具体过程其实也是一个dfs,以根节点为good点这种情况举例,此时先初始化st[1][1] = true,然后进入dfs,对于每个点都需要看其父节点的状态,如果st[fa][1] == true,那么当前点状态直接已知,肯定是st[now][1] = false,如果st[fa][1] = false,那么当前点可good也可不good,如果dp[now][1] > dp[now][0],那么st[now][1]就应该取good点更优,st[now][1] = true,如果dp[now][1] < dp[now][0],那么st[now][1]不取good点更优,st[now][1] = false,如果dp[now][1] == dp[now][0],再看dp2[now][1]和dp2[now][0],如果dp2[now][1] < dp2[now][0],st[now][1] = true,否则st[now][1] = false,对于根节点不为good点的情况也是一样的,只不过初始化st[1][0] = false。

最后特判一下n == 2的情况,因此此时不符合前面说的相邻两点间只有一个点能是good点。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#define int long long
using namespace std;int dp[200005][2], s[2], dp2[200005][2];
bool st[200005][2];
vector<int> to[200005];void dfs(int now, int fa){for(int i = 0; i < to[now].size(); i++){int v = to[now][i];if(v == fa) continue;dfs(v, now);}dp[now][1] = 1, dp[now][0] = 0;dp2[now][1] = to[now].size(), dp2[now][0] = 0;int mx = 0;for(int i = 0; i < to[now].size(); i++){int v = to[now][i];if(v == fa) continue;if(dp[v][1] > dp[v][0]){mx += dp[v][1];dp2[now][0] += dp2[v][1];}else if(dp[v][0] > dp[v][1]){mx += dp[v][0];dp2[now][0] += dp2[v][0];}else{mx += dp[v][0];dp2[now][0] += min(dp2[v][0], dp2[v][1]);}dp[now][1] += dp[v][0];dp2[now][1] += dp2[v][0];}dp[now][0] += mx;
}void dfs2(int now, int fa, int type){if(fa != 0){if(st[fa][type] == 1) st[now][type] = 0;else if(dp[now][0] > dp[now][1])st[now][type] = 0;else if(dp[now][1] > dp[now][0])st[now][type] = 1;else if(dp2[now][0] > dp2[now][1])st[now][type] = 1; elsest[now][type] = 0;}for(int i = 0; i < to[now].size(); i++){int v = to[now][i];if(v == fa) continue;dfs2(v, now, type);}
}signed main()
{int n;cin >> n;for(int i = 1; i < n; i++){int u, v;scanf("%lld%lld", &u, &v);to[u].push_back(v);to[v].push_back(u);}if(n == 2){puts("2 2\n1 1");return 0;}dfs(1, 0);printf("%lld ", max(dp[1][0], dp[1][1]));s[0] = s[1] = 0;//1无贡献时权值和 st[1][0] = 0;dfs2(1, 0, 0);//1有贡献时权值和st[1][1] = 1;dfs2(1, 0, 1);for(int i = 1; i <= n; i++){if(st[i][0]) s[0] += to[i].size();else s[0]++;}for(int i = 1; i <= n; i++){if(st[i][1]) s[1] += to[i].size();else s[1]++;                }if(dp[1][0] > dp[1][1]){printf("%lld\n", s[0]);for(int i = 1; i <= n; i++){if(st[i][0]) printf("%lld ", to[i].size());else printf("1 ");}}else if(dp[1][0] < dp[1][1]){printf("%lld\n", s[1]);for(int i = 1; i <= n; i++){if(st[i][1]) printf("%lld ", to[i].size());else printf("1 ");}}else{printf("%lld\n", min(s[0], s[1]));if(s[0] < s[1]){for(int i = 1; i <= n; i++){if(st[i][0]) printf("%lld ", to[i].size());else printf("1 ");}}else{for(int i = 1; i <= n; i++){if(st[i][1]) printf("%lld ", to[i].size());else printf("1 ");               }} }puts("");return 0;
}

[树形dp]Weight the Tree Codeforces1646D相关推荐

  1. 【CF1646D】D. Weight the Tree(树形dp、贪心)

    加权树 题意: 给定一颗树,让你给树上的点赋予权值.定义一个点的权值等于其所有相邻节点的权重之和时,这个点就是 good. 你需要找到一种赋值方法,使得树中 good 点数最多,同时所有顶点的权重总和 ...

  2. NYOJ 679 The Weight of Tree 搜索+dp+邻接表

    The Weight of Tree 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描述 456 has a tree of n nodes, each node is as ...

  3. 2021牛客多校4 - Rebuild Tree(树形dp)

    题目链接:点击查看 题目大意:给出一棵 nnn 个节点的树,现在可以删掉 kkk 条边,然后加上 kkk 条边,问有多少种方案使得操作后 nnn 个点仍然是一棵树 题目分析:原树删掉 kkk 条边后会 ...

  4. leetcode 1339. Maximum Product of Splitted Binary Tree | 1339. 分裂二叉树的最大乘积(树形dp)

    题目 https://leetcode.com/problems/maximum-product-of-splitted-binary-tree/ 题解 树形 dp,思路见草稿 /*** Defini ...

  5. Codeforces Round #263 (Div. 2) D. Appleman and Tree(树形DP)

    题目链接 D. Appleman and Tree time limit per test :2 seconds memory limit per test: 256 megabytes input ...

  6. Problem F. Grab The Tree HDU - 6324(树形dp+博弈)

    Little Q and Little T are playing a game on a tree. There are nn vertices on the tree, labeled by 1, ...

  7. Tree Cutting POJ - 2378(树形DP)

    题意:有n个谷仓有n-1条路连接,问最少删除哪几个点才能使得删除点后得到的连通图的加点数不大于n/2. 分析:求树的重心的变形题,poj3107的简单版,一遍dfs从叶子到根转移找出找到以每个节点为根 ...

  8. hdu-5834 Magic boy Bi Luo with his excited tree(树形dp)

    题目链接: Magic boy Bi Luo with his excited tree Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: ...

  9. 【POJ - 2378】Tree Cutting(树形dp,树的重心变形)

    题干: After Farmer John realized that Bessie had installed a "tree-shaped" network among his ...

最新文章

  1. Linux中常见shell命令总结
  2. ios开发 自定义btn_iOS一步步实现一个高度自定义UIButton控件
  3. c#如何wmf图片转换成png图片_每日一学:如何将png图片转换为jpg图片
  4. (转)令人无法理解的死锁案例分析
  5. 跨设备链路聚合_企业核心经常用到的链路聚合技术,原理与实现
  6. unity打开excel表格_Excel电子表格需要双击两次才能打开问题的解决方案
  7. 我的第一个WM5程序
  8. html判断安装没安装qq,QQ提示安装路径无效您没有权限怎么办 QQ2015提示安装路径无效您没有权限的解决方法...
  9. vscode风格个人主页源码
  10. 关于keil注册机的问题
  11. 鸿蒙系统的软件怎么下载,鸿蒙系统官网下载软件电脑版
  12. 微博超话显示服务器有点累,刚才手机刷微博,刷新了一下首页... - @菜菜_fz 的微博精选 - 微博国际站...
  13. PHP多用户商城系统 应该怎么选
  14. win11鼠标双击变成重命名解决方法
  15. 小技巧:使用谷歌地图查询公交和地铁信息
  16. mysql 关闭定时任务_mysql的定时任务实例教程
  17. 人工智能医学影像行业背景,智能医疗的发展背景
  18. 给排水册工程量清单指引
  19. JavaWeb实现登录验证码功能
  20. Mac使用delete键向后删除字符

热门文章

  1. 怎么编曲软件测试,关于编曲软件的测评
  2. 最短路径分析+路书(详细导航信息)
  3. 分布式控制系统(DCS)消耗的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  4. 用python轻松制作动态二维码
  5. 使用Apache CXF和Apache Axis2实现Web Services客户端
  6. 使用 EA 交易可视向导创建 EA 交易
  7. 餐掌柜SaaS项目实战-2022分布式微服务
  8. 【20180409】IT管理之IT十二条令
  9. 计算机读书报告Word,《读书报告模版》word版.doc
  10. 免费安全的内网穿透实现——Tailscale