• BZOJ2067 SZN


    题目大意

    传送门(yzhx在写这篇题解的时候bzoj崩了,只能挂这个了)

    给定一颗 n 个点的树,节点间距离为1, 求最少链覆盖,以及使在最少链覆盖的前提下最长链最短

    题解

    先求第一问:

    根据贪心的思想考虑每一个非树根节点,
    显然它可以选择一条连向儿子的边归为连向父亲的边所在的那条链
    (儿子数为奇数时必须选一条,偶数时可以不选,但其实这在第一问不重要),
    所以每个非树根节点的贡献是 儿子数量/2,
    而根节点若儿子数量为奇,则必须多生成一条链
    也就是 (儿子数量+1)/2

    再求第二问

    记录f[i]表示从底部开始算,第i号节点已经累积的未匹配的距离(也就是第一问中有奇数个儿子的叶子必须找出一条与父亲边组成链那种情况的累积长度)
    每次二分出最大值,然后遍历整棵树check即可
    遍历过程中值得注意的是: 对于每个节点,把它的每个儿子j按f[j]排序,然后按最大+最小,次大加次小组合起来,若有不合法或奇数个,则需二分出哪条边要向上合并

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define re register
    #define ll long long
    #define in inline
    #define get getchar()
    in int read()
    {
    	int t=0; char ch=get;
    	while(ch<'0' || ch>'9') ch=get;
    	while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    	return t;
    }
    const int _=1e5+6;
    struct edge{
    	int to,ne;
    }e[_];
    int h[_],n,tot,f[_],c[_],son[_];
    in void add(int x,int y)
    {
    	e[++tot].to=y,e[tot].ne=h[x],h[x]=tot;
    }
    in int check1(int l,int r,int x,int lim)
    {
    	while(l<r)
    	{
    		if(l==x)l++;if(r==x)r--;
    		if(son[l]+son[r]>lim) return 0;
    		l++,r--;
    	}
    	return 1;
    }
    in int dfs(int u,int fa,int lim)
    {
    	int cnt=0;
    	for(re int i=h[u];i;i=e[i].ne) {
    		int v=e[i].to;cnt++;
    		if(v==fa) continue;
    		if(!dfs(v,u,lim))return 0;
    		if(f[v]+1>lim) return 0;
    	}
    	if(cnt==1 && u!=1) {f[u]=0;return 1;}
    	cnt=0;
    	for(re int i=h[u];i;i=e[i].ne) {
    		int v=e[i].to;
    		if(v==fa)continue;
    		son[++cnt]=f[v]+1;
    	}
    	sort(son+1,son+cnt+1);
    	if(cnt%2==0) { //偶数个儿子
    		int flag=0;
    		for(re int i=1;i<=cnt/2;i++)
    			if(son[i]+son[cnt-i+1]>lim) flag=1; 
    		if(!flag) { f[u]=0;return 1;};//都合法
    		if(u==1) {return 0;}
    		int l=1,r=cnt-1,ans=-1;
    		while(l<=r) {
    			int mid=l+r>>1;
    			if(son[mid]>lim) {l=mid+1;continue;}
    			if(check1(1,cnt-1,mid,lim)) r=mid-1,ans=mid;
    			else l=mid+1;
    		} //二分出哪个向上连接
    		if(ans==-1) return 0;
    		f[u]=son[ans];return 1;
    	}
    	else { //奇数个儿子
    		if(cnt==1) {f[u]=son[1];return son[1]<=lim;}
    		int l=1,r=cnt,ans=-1;
    		while(l<=r) {
    			int mid=l+r>>1;
    			if(son[mid]>lim) {l=mid+1;continue;}
    			if(check1(1,cnt,mid,lim)) r=mid-1,ans=mid;
    			else l=mid+1;
    		}
    		if(ans==-1) return 0;
    		f[u]=son[ans];return 1;
    	}
    		
    }
    in int check(int mid)
    {
    	return dfs(1,0,mid);
    }
    int main()
    {
    	n=read();
    	for(re int i=1;i<n;i++) {
    		int x=read(),y=read();
    		add(x,y),add(y,x),c[x]++,c[y]++;
    	}
    	int ans=0;
    	for(re int i=2;i<=n;i++) ans+=(c[i]-1)/2;
    	ans+=(c[1]+1)/2;
    	cout<<ans<<' ';
    	int l=0,r=n-1;ans=0;
    	while(l<=r)
    	{
    		int mid=l+r>>1;
    		if(check(mid))
    			if(f[1]<=mid)r=mid-1,ans=mid;
    			else l=mid+1;
    		else
    			l=mid+1;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    
  • 相关阅读:
    递归神经网络(RNN)简介(转载)
    递归神经网络入门教程(转载)
    向量叉积的几何意义(转)
    向量点乘(内积)和叉乘(外积、向量积)概念及几何意义解读
    完全搞懂傅里叶变换和小波(6)――傅立叶级数展开之函数项级数的性质
    完全搞懂傅里叶变换和小波(5)——傅立叶级数展开之函数项级数的概念
    完全搞懂傅里叶变换和小波(4)——欧拉公式及其证明
    完全搞懂傅里叶变换和小波(3)——泰勒公式及其证明
    完全搞懂傅里叶变换和小波(2)——三个中值定理<转载>
    完全搞懂傅里叶变换和小波(1)——总纲<转载>
  • 原文地址:https://www.cnblogs.com/yzhx/p/11738313.html
Copyright © 2020-2023  润新知