• 【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;
    }
  • 相关阅读:
    【leetcode】299. 猜数字游戏
    【leetcode】300. 最长递增子序列
    【leetcode】223. 矩形面积
    【leetcode】222. 完全二叉树的节点个数
    【leetcode】229. 求众数 II
    【leetcode】215. 数组中的第K个最大元素
    【leetcode】221. 最大正方形
    【leetcode】216. 组合总和 III
    【leetcode】213. 打家劫舍 II
    【leetcode】210. 课程表 II
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7587283.html
Copyright © 2020-2023  润新知