hdu5044

题意:一个树上建两个加油站,使得所有点到达其最近加油站的最大距离最小。

解法:二分答案。关键时二分时候,要最合理话布局两个点的位置,做法是处理出来树的直径,然后在直径两端分别向中间移动二分的x步的两个点布下加油站。贪心可以证明正确性;

代码:

/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;

#define eps 1e-8
#define zero(_) (_<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=200010;
const LL INF=0x3FFFFFFF;
vector<int> vec[Max];
int n;
vector<int> diameter;
int rem[Max];
int rem1[Max];
int rem2[Max];
int dis[Max];
int st,en;
int dui[Max*2];
int now=0;
int count1=10;
void dfs(int u)
{
    memset(rem,0,sizeof rem);
    rem[u]=-1;
    int left=0;
    int right=1;
    dui[0]=u;
    dis[u]=0;
    while(left<right)
    {
        for(int i=0; i<vec[dui[left]].size(); i++)
        {
            if(rem[vec[dui[left]][i]]==0)
            {
                rem[vec[dui[left]][i]]=dui[left];
                dis[vec[dui[left]][i]]=dis[dui[left]]+1;
                dui[right++]=vec[dui[left]][i];
            }
        }
        left++;
    }
}

void ran1(int u,int* re)
{
    int left=0;
    int right=1;
    dui[0]=u;
    dis[u]=0;
    re[u]=count1;
    while(left<right)
    {
        if(dis[dui[left]]<now)
        for(int i=0; i<vec[dui[left]].size(); i++)
        {
            if(re[vec[dui[left]][i]]!=count1)
            {
                re[vec[dui[left]][i]]=count1;
                dis[vec[dui[left]][i]]=dis[dui[left]]+1;
                if(dis[vec[dui[left]][i]]<now)
                    dui[right++]=vec[dui[left]][i];
            }
        }
        left++;
    }
}

bool OK(int middle)
{
    count1++;
    now=middle;
    ran1(diameter[middle],rem1);
    ran1(diameter[diameter.size()-1-middle],rem2);
    for(int i=1; i<=n; i++)
    {
        if(rem1[i]!=count1&&rem2[i]!=count1)
            return false;
    }
    return true;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        for(int i=1;i<Max;i++)
        vec[i].clear();
        diameter.clear();
        scanf("%d",&n);
        for(int i=0; i<n-1; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            vec[a].push_back(b);
            vec[b].push_back(a);
        }
        dfs(1);
        int d=0;
        for(int i=1; i<=n; i++)
        {
            if(dis[i]>d)
            {
                d=dis[i];
                st=i;
            }
        }
        dfs(st);
        d=0;
        for(int i=1; i<=n; i++)
        {
            if(dis[i]>d)
            {
                d=dis[i];
                en=i;
            }
        }
        while(en!=-1)
        {
            diameter.push_back(en);
            en=rem[en];
        }
        int left=0,right=diameter.size();
        while(left<=right)
        {
            int middle=(left+right)/2;
            if(!OK(middle))
            {
                left=middle+1;
            }
            else
            {
                right=middle-1;
            }
        }
        int a=diameter[left];
        int b=diameter[diameter.size()-1-left];
        if(a==b)
        {
            a=b==n?n-1:b+1;
        }
        //cout<<diameter.size()<<" "<<OK(0)<<OK(1)<<endl;
        cout<<left<<" "<<a<<" "<<b<<'\n';
    }
    return 0;
}
/*
34
4
1 2
1 3
1 4
5
1 2
2 3
3 4
4 5
3
1 2
2 3
*/
时间: 2024-08-08 09:25:46

hdu5044的相关文章

hdu5044 Tree

Tree Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 845    Accepted Submission(s): 162 Problem Description You are given a tree (an acyclic undirected connected graph) with N nodes. The tree n

hdu5044 Tree 树链剖分,点剖分,边剖分,非递归版

//#pragma warning (disable: 4786) //#pragma comment (linker, "/STACK:16777216") //#pragma comment(linker, "/STACK:60400000,60400000") //HEAD #include <cstdio> #include <ctime> #include <cstdlib> #include <cstring&g

hdu5044(二分)

题意:一个树上建两个加油站.使得全部点到达其近期加油站的最大距离最小. 解法:二分答案.关键时二分时候,要最合理话布局两个点的位置,做法是处理出来树的直径,然后在直径两端分别向中间移动二分的x步的两个点布下加油站. 贪心能够证明正确性: 代码: /****************************************************** * @author:xiefubao ****************************************************

2014上海网络预选赛1003(树链剖分)HDU5044

Tree Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 700    Accepted Submission(s): 145 Problem Description You are given a tree (an acyclic undirected connected graph) with N nodes. The tree

HDU5029--Relief grain (树链剖分+线段树 )

题意:n个点构成的无根树,m次操作, 对于操作 x y z, 表示 x 到 y 路径上的 每个点 加一个 z 数字,可重复加.最后输出每个点 加的次数最多的那个数字,如果没有输出0. 赤裸裸的树链剖分,可是剖分之后 怎么用线段树维护每个点加的数字的次数呢.这里用到的思想类似于2014年上海网络赛的一道题.HDU5044,假如[x,y]这个区间上所有的点加上数字z,可以用两个标记 vec[x] + z,vec[y+1] -c.HDU上C++提交竟然爆栈,不过G++还是顺利ac了.具体见代码 1 #