zoukankan      html  css  js  c++  java
  • [BZOJ 2282] 消防

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2282

    Solution:

    看到球最大值最小   ------>   想到二分答案

    首先要推导出一些性质:

    1、这条路径一定在树的直径上

    感性证明:直径上的每个点通向直径末端的路径都是这个方向从该点出发的最长距离

    于是如果将答案路径引上直径的分支必定不会使答案更优

    2、设非直径点到直径的最长距离为$len_{not}$,选取的路径到直径两端的距离分别为$len_{on1}$,$len_{on2}$

    该路径的答案为$max(len_{not},len_{on1},len_{on2})$

    感性证明:如果路径长度达不到直径,同上述直径上点的性质,则新增的最远点只可能为直径的两端

    接下来先预处理$len_{not}$,以其为下界在直径上二分两端缩减的距离

    注意:此题结果只能为简单路径,因此如果$s>len_{直径}$,则输出$len_{not}$即可

    要仔细审题啊……

    Code:

    //by NewErA
    #include <bits/stdc++.h>
    
    using namespace std;
    typedef pair<int,int> P;
    
    const int MAXN=3e5+5;
    int n,s,d[MAXN],dia[MAXN],f[MAXN],rt1,rt2,cnt=0,D;
    bool on_dia[MAXN];
    vector<P> a[MAXN];
    
    void bfs(int st)
    {
        memset(d,-1,sizeof(d));
        d[st]=0;queue<int> q;q.push(st);
        while(!q.empty())
        {
            int cur=q.front();q.pop();
            for(int i=0;i<a[cur].size();i++)
            {
                P t=a[cur][i];
                if(d[t.first]==-1)
                {
                    if(on_dia[t.first]) d[t.first]=d[cur];
                    else d[t.first]=t.second+d[cur];
                    q.push(t.first);f[t.first]=cur;
                }
            }
        }
    }
    
    bool check(int k)
    {
        int l=1,r=cnt;
        while(dia[1]-dia[l+1]<=k && l<cnt) l++;
        while(dia[r-1]<=k && r>1) r--;
        return dia[l]-dia[r]<=s;
    }
    
    int main()
    {
        cin >> n >> s;
        for(int i=1;i<n;i++) 
        {
            int x,y,z;cin >> x >> y >> z;
            a[x].push_back(P(y,z));a[y].push_back(P(x,z));
        }
        bfs(1);for(int i=1;i<=n;i++) if(d[i]>d[rt1]) rt1=i;
        bfs(rt1);for(int i=1;i<=n;i++) if(d[i]>d[rt2]) rt2=i;  //求直径
        
        D=d[rt2];
        
        dia[++cnt]=d[rt2];on_dia[rt2]=true;
        while(rt2!=rt1)
        {
            rt2=f[rt2];
            dia[++cnt]=d[rt2];on_dia[rt2]=true;
        }
        bfs(rt2);  //处理出直径上的点,将其的d值设为0
        
        int l=0,r=D;
        for(int i=1;i<=n;i++) l=max(l,d[i]);
        if(s<D)
            while(l<=r)
            {
                int mid=(l+r)>>1;
                if(check(mid)) r=mid-1;
                else l=mid+1;
            }
        cout << l;
        return 0;
    }

    Review:

    1、最大值最小类的问题,使用二分答案来求解

    2、还是要注意审题啊,题目求的是简单路径

  • 相关阅读:
    八十五:redis之redis的事物、发布和订阅操作 (2019-11-18 22:54)
    八十四:redis之redis的集合、哈希操作
    八十三:redis之redis的字符串、过期时间、列表操作
    八十三:redis之redis的使用场景和安装
    八十二:memcached之python操作memcached
    八十一:memcached之telnet操作memcached
    八十:memcached之安装与参数
    MySQL篇之Navicat可视化工具
    MySQL数据库篇之多表查询
    MySQL数据库篇之单表查询
  • 原文地址:https://www.cnblogs.com/newera/p/9108963.html
Copyright © 2011-2022 走看看