• 【BZOJ2282】[Sdoi2011]消防 树形DP+双指针法+单调队列


    【BZOJ2282】[Sdoi2011]消防

    Description

    某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=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。

    题解:首先,选出的路径一定在某条直径上(可以用反证法证明)。

    然后我们将直径拎出来,假设我们最后选定的是直径上的[l,r]这部分,那么距离的最大值就是:

    max(直径的左端点到l的距离,直径的右端点到r的距离,[l,r]中每个节点的子树(不包含直径的部分)中到该节点最远的距离)

    前两个很好求,第三个怎么办?可以先预处理出每个点的子树中最远的距离,然后用单调队列查询最大值。然后就可以用双指针法来确定[l,r]了。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int maxn=300010;
    int n,m,cnt,len,r1,r2,h,t,ans;
    int to[maxn<<1],next[maxn<<1],head[maxn],val[maxn<<1],fa[maxn],p[maxn],vis[maxn],f[maxn],dep[maxn],q[maxn],v[maxn];
    void dfs(int x)
    {
    	if(dep[x]>dep[r2])	r2=x;
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])	fa[to[i]]=x,dep[to[i]]=dep[x]+val[i],dfs(to[i]);
    }
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    }
    int getv(int x)
    {
    	int tmp=dep[x];
    	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa[x])	tmp=max(tmp,getv(to[i]));
    	return tmp;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),m=rd();
    	int i,j,a,b,c;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
    	r2=1,dfs(1);
    	memset(dep,0,sizeof(dep)),memset(fa,0,sizeof(fa));
    	r1=r2,dfs(r1);
    	for(a=r2;a;p[++len]=a,vis[a]=1,a=fa[a]);
    	for(i=1;i<=len;i++)	v[i]=getv(p[i])-dep[p[i]];
    	h=1,t=0,ans=1<<30;
    	for(i=j=1;i<=len;i++)
    	{
    		for(;j<i&&dep[p[j]]-dep[p[i]]>m;j++);
    		while(h<=t&&v[q[t]]<=v[i])	t--;
    		q[++t]=i;
    		while(h<=t&&q[h]<j)	h++;
    		ans=min(ans,max(max(dep[p[i]]-dep[p[len]],dep[p[1]]-dep[p[j]]),v[q[h]]));
    	}
    	printf("%d",ans);
    	return 0;
    }
  • 相关阅读:
    HDU 2089 不要62
    HDU 5038 Grade(分级)
    FZU 2105 Digits Count(位数计算)
    FZU 2218 Simple String Problem(简单字符串问题)
    FZU 2221 RunningMan(跑男)
    FZU 2216 The Longest Straight(最长直道)
    FZU 2212 Super Mobile Charger(超级充电宝)
    FZU 2219 StarCraft(星际争霸)
    FZU 2213 Common Tangents(公切线)
    FZU 2215 Simple Polynomial Problem(简单多项式问题)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7587283.html
Copyright © 2020-2023  润新知