bzoj2067: [Poi2004]SZN

传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2067

思路:首先第一问就是最少多少笔画完这个图,ans=1+Σ(deg[i]-1)/2

第二问显然可以二分+判定。

先二分最长长度限制lim

怎么判定呢?

对于每个点,把它子树所有点向上需要的答案统计出来到a[]中,如果子树个数是偶数,则额外加一个a[i]=0

然后对a排序,二分删掉a中的一个元素,从大到小匹配判断是否合法,如果任何方案都不合法,则是不合法的直接退出

如果弄到最后都合法,这个答案就合法,不过要注意判断根的时候如果子树是偶数个不能额外加元素,也不能二分判断,而要直接判断合法性,否则两个点会错

--http://lbn187.is-programmer.com/posts/179515.html#


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int maxn=10010,maxm=20010;
using namespace std;
int n,pre[maxm],now[maxn],son[maxm],deg[maxn],tot,res=1,cnt,va[maxn],a[maxn];//a[i]儿子节点连上来的链的长度,va[i]i下面的链的最大值 
void add(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,deg[b]++;}
bool check(int x,int lim){
	for (int l=1,r=cnt;l<=r;l++,r--){//贪心,第k大和第k小两两配对 
		if (l==x) l++;
		if (r==x) r--;
		if (a[l]+a[r]>lim) return 0;
	}
	return 1;
}

bool can(int x,int fa,int lim){
	//printf("%d %d %d\n",x,fa,lim);
	int ans=0;
	for (int y=now[x];y;y=pre[y]) 
		if (son[y]!=fa)
			if (!can(son[y],x,lim)) 
				return 0;
	cnt=0;
	for (int y=now[x];y;y=pre[y]) if (son[y]!=fa) a[++cnt]=va[son[y]]+1;
	if (x==1&&cnt%2==0){//根节点因为上面没有点来check,所以为偶数时要判断两两配对方案是否合法 
		va[x]=0,sort(a+1,a+1+cnt);
		for (int l=1,r=cnt;l<r;l++,r--) va[x]=max(va[x],a[l]+a[r]);
		return va[x]<=lim;
	}
	if (!(cnt&1)) a[++cnt]=0;sort(a+1,a+1+cnt);//如果为偶数,加一个为0的点 
	for (int l=1,r=cnt,mid;l<=r;){
		mid=(l+r)>>1;
		if (check(mid,lim)) ans=mid,r=mid-1;//二分不配对的节点 
		else l=mid+1;
	}
	if (!ans) va[x]=1e9;else va[x]=a[ans];
	return va[x]<=lim;
}

int main(){
	while (scanf("%d",&n)!=EOF){int ans=0;
		memset(now,0,sizeof(now)),memset(va,0,sizeof(va)),tot=0,res=1,memset(deg,0,sizeof(deg)),memset(a,0,sizeof(a));
		for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
		for (int i=1;i<=n;i++) res+=(deg[i]-1)>>1;
		for (int l=1,r=n-1,mid;l<=r;){
//			printf("fuckpp%d\n",(l+r)>>1);
			mid=((l+r)>>1);
			if (can(1,0,mid)) ans=mid,r=mid-1;
			else l=mid+1;
		}
		printf("%d %d\n",res,ans);
	}
	return 0;
}


转载于:https://www.cnblogs.com/thythy/p/5493504.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值