codeforce 1041 E. Tree Reconstruction 思维题

E. Tree Reconstruction
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Monocarp has drawn a tree (an undirected connected acyclic graph) and then has given each vertex an index. All indices are distinct numbers from 11) — vertices connected by an edge.

Note: The numeration of edges doesn't matter for this task. Your solution will be considered correct if your tree produces the same pairs as given in the input file (possibly reordered). That means that you can print the edges of the tree you reconstructed in any order.

Examples
Input
Copy
4
3 4
1 4
3 4
Output
Copy
YES
1 3
3 2
2 4
Input
Copy
3
1 3
1 3
Output
Copy
NO
Input
Copy
3
1 2
2 3
Output
Copy
NO
Note

Possible tree from the first example. Dotted lines show edges you need to remove to get appropriate pairs.

题意: 给出一棵树和一个K 现在要将这棵树的叶子节点分为n个集合,使得每个集合内的任意两个节点的距离不大于k

解题思路:
首先,要很容易想到,大致算法是用dfs 一层一层的合并。
假设S[u] 为以u节点为跟的所有叶子节点的最小分割集合。
假设现在搜到了u节点,我们能知道u节点的所有儿子节点的S[son[u]]
考虑如何是否能贪心的去合并。
假设我们能知道每个叶子节点到u的距离,那我们就可以根据这个给S[son[u]] 排序
从小到大依次判断是否能合并。
想到这里的话,如果用启发式合并 可以做到nlognlogn的复杂度 要用到set,常数较大,感觉过不了。
但再仔细想(看)一(题)下(解) 对于每个子集合最多只有一个集合能合并到其他兄弟节点的集合里。
所以我们只需要记录下S[u] 中 最大叶子节点深度最小 的那个子集合深度就可以了。
具体可以看代码。

#include <bits/stdc++.h>
using namespace std;
class Edge{
public:
    int v,next;
};
const int MAX = 1e6+10;
Edge edge[MAX<<1];
int head[MAX];
int tot;
void init(){
    memset(head,-1,sizeof head);
    tot=0;
}
void add(int u,int v){
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot;
    tot++;
}
int ans=0;
int n,k;
int dfs(int u,int pre){
    priority_queue<int> PQ;
    for(int i= head[u];i!=-1;i=edge[i].next){
        int v= edge[i].v;
        if(v==pre) continue;
        int cnt = dfs(v,u)+1;
        PQ.push(-cnt);
    }
    int cnt1=0,cnt2=0;
    if(!PQ.empty()){
        cnt1=-PQ.top();
        PQ.pop();
    }
    while(!PQ.empty()){
        cnt2= -PQ.top();
        PQ.pop();
        if(cnt1+cnt2<=k){
            cnt1=cnt2;
            ans--;
        }else{
            break;
        }
    }
    //cout<<u<<":"<<cnt1<<endl;
    return cnt1;
}
int du[MAX];
int main(){
    int root=1;
    init();
    scanf("%d %d",&n,&k);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
        du[v]++;
        du[u]++;
        if(du[v]>=2){
            root=v;
        }
        if(du[u]>=2){
            root=u;
        }
    }
    ans=0;
    for(int i=1;i<=n;i++){
        ans+=(du[i]==1);
    }
    dfs(root,-1);
    cout<<ans<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值