• 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
    ",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
    ",(l+r)>>1);
    			mid=((l+r)>>1);
    			if (can(1,0,mid)) ans=mid,r=mid-1;
    			else l=mid+1;
    		}
    		printf("%d %d
    ",res,ans);
    	}
    	return 0;
    }
    


  • 相关阅读:
    docker基于commit命令创建支持ssh服务的镜像
    CentOS 6.5 下Nginx服务的安装与配置
    CentOS 6.5 下搭建vsftp服务
    CentOS 6.5 下搭建FastDFS服务
    CentOS 6.5 下Nginx服务的安装与配置
    CentOS 6.5 下搭建NTP服务器
    CentOS6.5 下Haproxy服务的安装与配置
    CentOS 6.5 下HeartBeat的安装与配置
    CentOS 6.5 下keepalived服务的配置
    IDE vscode识别webpack中alias配置路径
  • 原文地址:https://www.cnblogs.com/thythy/p/5493504.html
Copyright © 2020-2023  润新知