zoukankan      html  css  js  c++  java
  • [POI2004] SZN

    Description

    给定(N(Nleq 10000))个点的树,要求用最少的路径覆盖树边。路径之间可以有交点,不能有交边。问最少需要几条路径以及在第一问的基础上最长的路径最短是多少?

    Solution

    第一问对于每个点考虑它与它的孩子之间的边

    如果能两两消掉是最好的,贡献就是(frac{deg[i]-1}2),否则就要额外多出一条边,贡献是(frac{deg[i]-1}2+1)

    第二问就是二分答案了

    判断(leq mid) 是否可行

    可以一遍 (dfs) 求出 (f[i]) 表示第 (i) 个点最短向下延伸多长

    如何求这个 (f[i])

    需要对点的孩子个数分奇偶性讨论

    如果它有偶数个孩子,那么最理想的情况就是让他们两两匹配,这个点的 (f) 值为 (0)

    如果事与愿违(我写个博客怎么都这么多骚话),就可以尝试拿出两个孩子,让一个孩子的链断在这个点,这个点接上另一个孩子的链,其他孩子两两匹配。

    如果有奇数个孩子,那就是拿出一个孩子,让这个点接上这个孩子的链,其他孩子两两匹配。

    判断两两匹配可以再套个二分。

    Code

    #include<set>
    #include<map>
    #include<cmath>
    #include<queue>
    #include<cctype>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using std::min;
    using std::max;
    using std::swap;
    using std::vector;
    const int N=10005;
    typedef double db;
    typedef long long ll;
    #define pb(A) push_back(A)
    #define pii std::pair<int,int>
    #define mp(A,B) std::make_pair(A,B)
     
    int head[N],f[N];
    int n,cnt,g[N],deg[N];
     
    struct Edge{
        int to,nxt;
    }edge[N<<1];
     
    void add(int x,int y){
        edge[++cnt].to=y;
        edge[cnt].nxt=head[x];
        head[x]=cnt;
    }
     
    int getint(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch))w|=ch=='-',ch=getchar();
        while( isdigit(ch))X=X*10+ch-48,ch=getchar();
        if(w) return -X;return X;
    }
     
    bool check(int l,int r,int x,int mid){
        while(l<r){
            if(l==x) l++;
            if(r==x) r--;
            if(g[l]+g[r]>mid) return 0;
            l++,r--;
        } return 1;
    }
     
    bool dfs(int now,int fa,int x){
        int cnts=0;
        for(int i=head[now];i;i=edge[i].nxt){
            int to=edge[i].to;cnts++;
            if(to!=fa) if(!dfs(to,now,x)) return 0;
            if(f[to]+1>x) return 0;
        } if(cnts==1 and now!=1) return f[now]=0,1;
        int pos=0;
        for(int i=head[now];i;i=edge[i].nxt){
            int to=edge[i].to;
            if(to==fa) continue;
            g[++pos]=f[to]+1;
        } std::sort(g+1,g+1+pos);
        if(pos%2==0){ //偶数
            int flag=0;
            for(int i=1;i<=pos/2;i++)
                if(g[i]+g[pos-i+1]>x)
                    flag=1;
            if(!flag) return f[now]=0,1;
            if(now==1) return 0;
            int l=1,r=pos-1,ans=0;
            while(l<=r){
                int mid=l+r>>1;
                if(g[mid]>x) {l=mid+1;continue;}
                if(check(1,pos-1,mid,x)) ans=mid,r=mid-1;
                else l=mid+1;
            } if(!ans) return 0;
            f[now]=g[ans];return 1;
        } else{
            if(pos==1) return g[1]<=x?f[now]=g[1],1:0;
            int l=1,r=pos,ans=0;
            while(l<=r){
                int mid=l+r>>1;
                if(g[mid]>x) {l=mid+1;continue;}
                if(check(1,pos,mid,x)) ans=mid,r=mid-1;
                else l=mid+1;
            } if(!ans) return 0;
            f[now]=g[ans];return 1;
        }
    }
     
    signed main(){
        // system("fc 11.out out.txt");
        // freopen("11.in","r",stdin);//freopen("out.txt","w",stdout);
        n=getint();
        for(int i=1;i<n;i++){
            int x=getint(),y=getint();
            add(x,y);add(y,x);deg[x]++;deg[y]++;
        } int ans=1;
        for(int i=1;i<=n;i++) ans+=(deg[i]-1)/2;
        printf("%d ",ans);
        int l=0,r=n-1;ans=0;
        while(l<=r){
            int mid=l+r>>1;
            if(dfs(1,0,mid)){
                if(f[1]<=mid) ans=mid,r=mid-1;
                else l=mid+1;
            }
            else l=mid+1;
        } printf("%d
    ",ans);
        return 0;
    }//youngneal
    
  • 相关阅读:
    【转】【VS2008无法启动asp.net development server】的解决
    C#运用技巧(1)
    C# — WinForm 基本控件
    TB 需求分析
    C# 远程连接SQL 2005数据库
    SQL语句的运用
    如何跌倒
    国学堂-梁冬对话张长琳《人体的彩虹》系列
    帝范:中国最伟大帝王的沉思录
    web.xml 配置
  • 原文地址:https://www.cnblogs.com/YoungNeal/p/9794394.html
Copyright © 2011-2022 走看看