[BZOJ2067][Poi2004]SZN:二分答案

分析:

玩过一笔画类型游戏的人都知道,本题第一问答案为(∑(deg[i]&1))/2。
但是更能揭示这道题做法的公式是∑((deg[i]-1)/2)+1。
可以理解为把每一个节点的子树通过删链使其成为一条链。
于是我们就可以愉快地分治+二分答案了。
注意当一个节点有偶数个孩子节点时需要补一条长为0的链。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>

const int MAXN=10005;
int n,ecnt,head[MAXN],deg[MAXN];
int len[MAXN],b[MAXN],siz;
struct Edge{
    int to,nxt;
}e[MAXN<<1];

inline void add_edge(int bg,int ed){
    ecnt++;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    head[bg]=ecnt;
}

inline bool check2(int mid,int siz,int lim){
    int lp=1,rp=siz;
    if(lp==mid) lp++;
    if(rp==mid) rp--;
    while(lp<rp){
        if(b[lp]+b[rp]>lim) return 0;
        lp++;rp--;
        if(lp==mid) lp++;
        if(rp==mid) rp--;
    }
    return 1;
}

bool dfs(int x,int pre,int lim){
    bool isleaf=1;
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==pre) continue;
        isleaf=0;
        if(!dfs(ver,x,lim)) return 0;
    }
    if(isleaf){
        len[x]=1;
        return 1;
    }
    siz=0;
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==pre) continue;
        b[++siz]=len[ver];
    }
    if(x==1){
        std::sort(b+1,b+siz+1);
        if(siz&1){
            if(b[siz]>lim) return 0;
            siz--;
        }
        int lp=1,rp=siz;
        while(lp<rp){
            if(b[lp]+b[rp]>lim) return 0;
            lp++;rp--;
        }
        return 1;
    }       
    if(!(siz&1)) b[++siz]=0;
    std::sort(b+1,b+siz+1);
    int l=1,r=siz,res=-1;
    while(l<=r){
        if(b[l]>=lim) return 0;
        int mid=((l+r)>>1);
        if(check2(mid,siz,lim)) res=mid,r=mid-1;
        else l=mid+1;
    }
    if(res==-1||b[res]>=lim) return 0;
    len[x]=b[res]+1;
    return 1;
}

inline bool check1(int mid){
    return dfs(1,0,mid);
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
        deg[u]++;deg[v]++;
    }
//  int ans1=1;
//  for(int i=1;i<=n;i++)
//      ans1+=(deg[i]-1)/2;
    int ans1=0;
    for(int i=1;i<=n;i++)
        ans1+=(deg[i]&1);//两种求法均可。
    printf("%d ",ans1>>1);
    int l=1,r=10000,ans2;
    while(l<=r){
        int mid=((l+r)>>1);
        if(check1(mid)) ans2=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans2);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9688504.html
今日推荐