Description

给定\(N(N\leq 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\n",ans);
    return 0;
}//youngneal
10-15 20:22