zoukankan      html  css  js  c++  java
  • bzoj 2282 消防

    Written with StackEdit.

    Description

    某个国家有(n)个城市,这(n)个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为(z_i(z_i<=1000))

    这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。

    现在这个国家的经费足以在一条边长度和不超过(s)的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。

    你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

    Input

    输入包含(n)行:
    (1)行,两个正整数(n)(s),中间用一个空格隔开。其中(n)为城市的个数,(s)为路径长度的上界。设结点编号以此为(1,2,……,n)
    从第(2)行到第(n)行,每行给出(3)个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,(“2) (4) (7”)表示连接结点(2)(4)的边的长度为(7)

    Output

    输出包含一个非负整数,即所有城市到选择的路径的最大值,当然这个最大值必须是所有方案中最小的。

    Sample Input

    【样例输入1】
    5 2
    1 2 5
    2 3 2
    2 4 4
    2 5 3

    【样例输入2】
    8 6
    1 3 2
    2 3 2
    3 4 6
    4 5 3
    4 6 4
    4 7 2
    7 8 3

    Sample Output

    【样例输出1】

    5
    【样例输出2】

    5

    HINT

    对于(100\%)的数据,(n<=300000),边长(<=1000)

    Solution

    • 有一个性质:这样的路径一定是直径上的某一段.
    • 那么先通过两次(bfs)找出直径,二分答案.
    • 根据二分的答案在直径两侧向内取,取出后验证长度是否小于等于(s).
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LoveLive;
    inline int read()
    {
    	int out=0,fh=1;
    	char jp=getchar();
    	while ((jp>'9'||jp<'0')&&jp!='-')
    		jp=getchar();
    	if (jp=='-')
    		{
    			fh=-1;
    			jp=getchar();
    		}
    	while (jp>='0'&&jp<='9')
    		{
    			out=out*10+jp-'0';
    			jp=getchar();
    		}
    	return out*fh;
    }
    const int MAXN=3e5+10;
    int n,s;
    int cnt=0,head[MAXN];
    int nx[MAXN<<1],to[MAXN<<1],val[MAXN<<1];
    inline void add(int u,int v,int w)
    {
    	++cnt;
    	to[cnt]=v;
    	nx[cnt]=head[u];
    	val[cnt]=w;
    	head[u]=cnt;
    }
    int dis[MAXN],pre[MAXN];
    int marked[MAXN];
    void bfs(int rt)
    {
    	memset(dis,-1,sizeof dis);
    	dis[rt]=0;
    	queue<int> q;
    	q.push(rt);
    	while(!q.empty())
    		{
    			int u=q.front();
    			q.pop();
    			for(int i=head[u];i;i=nx[i])
    				{
    					int v=to[i];
    					if(dis[v]!=-1)
    						continue;
    					pre[v]=u;
    					if(marked[v])
    						dis[v]=dis[u];
    					else
    						dis[v]=dis[u]+val[i];
    					q.push(v);
    				}
    		}
    }
    int top=0,st[MAXN];
    int check(int d)
    {
    	int l=1,r=top;
    	while(st[1]-st[l+1]<=d && l<=top)
    		++l;
    	while(st[r-1]<=d && r>=1)
    		--r;
    	return st[l]-st[r]<=s;
    }
    int main()
    {
    	n=read(),s=read();
    	for(int i=1;i<n;++i)
    		{
    			int u=read(),v=read(),w=read();
    			add(u,v,w);
    			add(v,u,w);
    		}
    	int rt=0,x=0;
    	bfs(1);
    	for(int i=1;i<=n;++i)
    		if(dis[rt]<dis[i])
    			rt=i;
    	bfs(rt);
    	for(int i=1;i<=n;++i)
    		if(dis[x]<dis[i])
    			x=i;//from rt to x
    	int L=0,R=dis[x];
    	st[++top]=dis[x];
    	marked[x]=1;
    	while(x!=rt)
    		{
    			st[++top]=dis[pre[x]];
    			x=pre[x];
    			marked[x]=1;
    		}
    	bfs(x);
    	for(int i=1;i<=n;++i)
    		L=max(L,dis[i]);
    	if(s<R)
    		{
    			while(L<=R)
    				{
    					int mid=(L+R)>>1;
    					if(check(mid))
    						R=mid-1;
    					else
    						L=mid+1;
    				}
    		}
    	printf("%d
    ",L);
    	return 0;
    }
    

    参考了hzwer的blog.

  • 相关阅读:
    Cloudera Manager安装之利用parcels方式安装3或4节点集群(包含最新稳定版本或指定版本的安装)(添加服务)(CentOS6.5)(五)
    Cloudera Manager安装之利用parcels方式安装单节点集群(包含最新稳定版本或指定版本的安装)(添加服务)(CentOS6.5)(四)
    Cloudera Manager安装之Cloudera Manager 5.3.X安装(三)(tar方式、rpm方式和yum方式)
    Cloudera Manager安装之时间服务器和时间客户端(二)
    Cloudera Manager安装之Cloudera Manager安装前准备(CentOS6.5)(一)
    Cloudera Manager架构原理
    Cloudera Manager是啥?主要是干啥的?
    HDU-1664-Different Digits(BFS)
    AdapterView&lt;?&gt; arg0, View arg1, int arg2, long arg3參数含义
    c++11 新特性之 autokeyword
  • 原文地址:https://www.cnblogs.com/jklover/p/10062026.html
Copyright © 2011-2022 走看看