bzoj 2067 [Poi2004]SZN——二分+贪心

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2067

最少的线段可以贪心地想出来。(结果还是写错了)就是偶数孩子可以自己配对,奇数孩子要带一个上去;算条数的时候在该条拐弯或截止的时候算,就是每个点的度数减1除以2求和,最后加上1表示根节点。

还以为第二问能贪心做呢。结果WA。奇数孩子带一个最小的上去是不行的;偶数孩子都不带上去也是不行的。

于是二分一下答案。结果WA。偶数孩子带一个尽量小的上去还能贪心,奇数孩子并不是用中间那个孩子与别的孩子配对使得自己带一个尽量小的孩子上去。

奇数孩子不知带哪个孩子上去,像这种就应该考虑枚举、二分之类暴力一点的做法。枚举的话可能n^2,所以二分一下,判断就是去掉这个孩子,剩下的贪心配对。

顺便把偶数孩子的那个“带0上去”也作为一个孩子加进去,就很方便了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+5;
int n,hd[N],xnt,to[N<<1],nxt[N<<1],rt,l,r,mid;
int deg[N],dis[N],ans,sta[N],top;
bool flag;
int rdn()
{
    int ret=0;bool fx=1;char ch=getchar();
    while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘) ret=(ret<<3)+(ret<<1)+ch-‘0‘,ch=getchar();
    return fx?ret:-ret;
}
void add(int x,int y)
{
    to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt;
    to[++xnt]=x; nxt[xnt]=hd[y]; hd[y]=xnt;
    deg[x]++; deg[y]++;
}
void dfs(int cr,int fa)
{
    for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
        {
            dfs(v,cr);
            if(flag)return;
        }

    top=0;
    for(int i=hd[cr];i;i=nxt[i])
        if(to[i]!=fa)
            sta[++top]=dis[to[i]]+1;
    if(deg[cr]&1) sta[++top]=0;//偶数孩子可以0地向上
    sort(sta+1,sta+top+1);

    if(cr==1)
    {
        for(int i=1,j=top;i<j;i++,j--)
            if(sta[i]+sta[j]>mid)
            {flag=1;return;}
        return;
    }

    int tl=1,tr=top,ret=-1;
    while(tl<=tr)
    {
        int tmid=tl+tr>>1; bool fg=0;
        for(int i=1,j=top;i<j;i++,j--)
        {
            if(i==tmid) i++; if(j==tmid) j--;
            if(sta[i]+sta[j]>mid){fg=1;break;}
        }
        if(!fg) ret=tmid,tr=tmid-1;
        else tl=tmid+1;
    }
    if(ret==-1)flag=1;
    else dis[cr]=sta[ret];
}
int main()
{
    n=rdn();
    for(int i=1,u,v;i<n;i++)
    {
        u=rdn(); v=rdn(); add(u,v);
    }
//    for(int i=2;i<=n;i++) ans+=deg[i]>>1;
//    ans+=(deg[1]==1?0:(deg[1]+1)>>1);
    for(int i=1;i<=n;i++) ans+=((deg[i]-1)>>1);
    printf("%d ",ans+1);
    r=n;
    while(l<=r)
    {
        mid=l+r>>1; flag=0;
        dfs(1,0);
        if(!flag) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/9690512.html

时间: 2024-08-30 09:53:44

bzoj 2067 [Poi2004]SZN——二分+贪心的相关文章

【BZOJ2067】[Poi2004]SZN 二分+树上贪心

[BZOJ2067][Poi2004]SZN Description String-Toys joint-stock 公司需要你帮他们解决一个问题. 他们想制造一个没有环的连通图模型. 每个图都是由一些顶点和特定数量的边构成. 每个顶点都可以连向许多的其他顶点.一个图是连通且无环的. 图是由许多的线做成的.一条线是一条连接图中两个顶点之间的路径.由于一些技术原因,两条线之间不能有重叠的部分,要保证图中任意一条边都被且仅被一条线所覆盖.由于一些技术原因,做一个这样的图的模型的费用取决于用了多少条线

BZOJ 2072 POI2004 MOS 动态规划+贪心

题目大意:过桥问题 我们考虑利用时间最小的两个人倒运,把时间大的人依次送过去 有两种方式: 1.时间最小的人和时间最大的人过去,然后时间最小的人把火把拿回来 2.时间最小和第二小的两个人过去,然后时间最小的人把火把拿回来:接着时间最大和第二大的两个人过去,时间第二小的人把火把拿回来 为了保证最优 运输应该不外乎这两种形式 那么令f[i]表示当前没有过桥的人还剩i个时的最短时间 DP即可 #include <cstdio> #include <cstring> #include &l

bzoj 2067 [ Poi 2004 ] SZN —— 二分

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2067 问题1:贪心考虑,应该是每个点的儿子尽量两两配对,如果剩一个就和自己合并向上,所以 ans = 1 + ∑(1<= i <= n ) (deg[i] - 1)/2 问题2:二分最长线段的长度,设 f[x] 表示自己带着的链的长度(即儿子中剩下的那一个带来的长度),判断是否满足条件即可: 如果当前节点有偶数个儿子,那么加一个 f 值为0的,进行二分: 注意根要单独判断,因为不能向上带

二分+贪心

上海邀请赛热身时候,C题是一个二分+贪心的题目.起初并不会,问了旁边的复旦大神.这几天无意发现VJ上一个专题.擦原来是一个经典类型. 二分+贪心 这类题目注意数据范围,1e8,1e9一般都是这样. 注意事项 二分法有很多写法,推荐用lf+1 < rf的写法.这个也符合计算机中数据存取的原则.对于浮点数,直接就循环100次,精度绝对够. 一般有两种类型,一种是询问最优,即数列中无重复.一种是多个即lower_bound ,upper_bound这类函数问题. 贪心使用,就是这个问题枚举答案可被验证

nyoj586||poj2456 二分+贪心

完全看不懂题意....百度搜搜才看懂题意  然后就参考代码了 和yougth的最大化()nyoj914差不多的方法 二分+贪心 #include <stdio.h> #include <algorithm> using namespace std; int c,a[100005],n; bool judge(int k) { int p=a[0],cnt=1;//也就这里注意点 从1开始 自己想想为啥 for(int i=1;i<n;i++) { if(a[i]-p>=

HDU 4004 The Frog&#39;s Games 二分 贪心

戳这里:HDU 4004 //思路:二分经典入门题...贪心判方案是否可行 1 #include "bits/stdc++.h" 2 using namespace std; 3 int L, n, m; 4 int pos[500010], dis[500010]; 5 6 bool Cant(int Dis_Jump) 7 { 8 int i, Dis_Sum = 0, Count = 0; 9 for(i = 1; i <= n + 1; ++i) { 10 if(dis[

贪心(bnuoj49103+二分+贪心)

贪心 小明喜欢养小鸡,小鸡喜欢吃小米.小明很贪心,希望养s只不同种类的小鸡,小鸡也很贪心,每天除了吃固定的ai粒小米外,还想多吃bi*s粒小米. 小明每天有M(0<=M<=10^9)粒小米可以喂小鸡,小鸡共有N(0<=N<=1000)种.问小明最多可以养多少只小鸡? Input 多组数据,请读到文件尾 第一行,整数N,M,以空格分隔,之后两行,第一行为N个整数ai,第二行为N个整数bi. ai.bi都在int范围内 Output 一行一个整数,s. Sample Input 2 4

poj1505Copying Books 二分+贪心详细总结

前两天花了时间理解了nyoj的586疯牛和nyoj619青蛙过河,满以为自己能重新写出这道题...谁知道..... 题意:有m本书,k个人来抄,每本书有一个书本页数:求使得k个人抄完的最大页数最小,并且每个人都至少要抄一本,然后输出抄书的方案 分析: 这里又涉及到前面nyoj的586疯牛和nyoj619青蛙过河说过的最大值中的最小值,  用前面的例子去理解比较方便 1.我们应该先用二分+贪心算出一个最大页数的最小值--这里和前面的类似 在二分的过程中,我们对于当前考虑的值 x  划分人数的贪心过

【bzoj2097】[Usaco2010 Dec]Exercise 奶牛健美操 二分+贪心

题目描述 Farmer John为了保持奶牛们的健康,让可怜的奶牛们不停在牧场之间 的小路上奔跑.这些奶牛的路径集合可以被表示成一个点集和一些连接 两个顶点的双向路,使得每对点之间恰好有一条简单路径.简单的说来, 这些点的布局就是一棵树,且每条边等长,都为1. 对于给定的一个奶牛路径集合,精明的奶牛们会计算出任意点对路径的最大值, 我们称之为这个路径集合的直径.如果直径太大,奶牛们就会拒绝锻炼. Farmer John把每个点标记为1..V (2 <= V <= 100,000).为了获得更加