codeforces 1042 F : Leaf Sets

题意:给定一棵树,将叶子节点划分成若干个集合,集合内的叶子节点两两距离小于$K$,问最小划分的集合数。

想歪了,感觉这类题目自己做得好虚啊,不知道为什么能想到这样的思路。

正解就是对于每一个儿子,保存最大距离的叶子,然后排序之后,找到一$R$使得$[1,R]$可以在一个集合内。

然后将$[R+1,son]$内的点都各自成立一个集合。然后这个节点作为儿子的返回值为R点的$dis+1$。

证明:显然的一点是将$[1,R]$内的点并起来是优的。我们只需要证明为什么直接把$[R+1,son]$单独构建最优即可。

若$R+1$和子树外的一个点$a$在一个集合内,$R$和子树外一个点$b$在一个集合内,我们只需证明$a b R$能在一个集合内即可。

只需要根据$dis[R][R+1]>dis[a][R+1]$来证明即可。

$O(n*logn)$

#include <bits/stdc++.h>
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define for1(a,b,i) for(int i=a;i<=b;++i)
#define FOR2(a,b,i) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
inline int read() {
    int f=1,sum=0;
    char x=getchar();
    for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1;
    for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0';
    return f*sum;
}

#define M 1000005
int n,m;
int ans,e_size,head[M],du[M];

struct node {int v,nxt;}e[M*2];

inline void e_add(int u,int v) {
    e[++e_size]=(node){v,head[u]};
    head[u]=e_size;
}

inline int dfs(int x,int fa) {
    vector <int> sta;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(v==fa) continue;
        sta.push_back(dfs(v,x)+1);
    }
    if(!sta.size()) return 0;
    sort(sta.begin(),sta.end());
    int size=sta.size();
    while (size>=2) {
        //cout<<size<<" "<<sta.size()<<endl;
        if(sta[size-2]+sta[size-1]<=m) break;
        --size,++ans,sta.pop_back();
    }
    return sta[size-1];
}

int main () {
    //freopen("a.in","r",stdin);
    n=read(),m=read();
    for1(2,n,i) {
        int x=read(),y=read();
        ++du[x],++du[y];
        e_add(x,y),e_add(y,x);
    }
    int root=0;
    for1(1,n,i) if(du[i]>1) root=i;
    dfs(root,0);
    cout<<ans+1<<endl;
} 
posted @ 2018-09-18 08:46  asd123www  阅读(484)  评论(0编辑  收藏  举报