zoukankan      html  css  js  c++  java
  • [BZOJ 3124] 直径

    Link:

    BZOJ 3124 传送门

    Solution:

    对于第二问,要先推出几个性质:

    1、这些边在一条直径上

    2、这些边一定是连续的

    这类性质主要就靠瞎蒙再用反证法证一证就好了

    (证不出来感性认知一下就直接上吧

    接下来只要在任意一个直径上寻找连续的可行边即可

    设直径的两个端点分别为$S,T$,可以使用尺取法的思路,维护左右两个边界

    对于每个点判断其是否有一条不经过直径但与该点到$S/T$距离相同的路径,如果有,则将左/右边界移动

    (这里有一个剪枝,如果是从$T->S$判断,只要有点$x$有第二条距离为$dist(S,x)$的路径,则可以直接退出,可用反证法证明)

    这里的思路有些类似于BZOJ 2282

    但此题对于每个节点的判断不具有决策单调性,因此不能二分

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef long long ll;
    const int MAXN=2e5+10;
    bool on_dia[MAXN];
    struct edge{int nxt,to;ll w;}e[MAXN<<2];
    ll dia,dist[MAXN];
    int n,head[MAXN],rt1,rt2,f[MAXN],tot=0,cur=0;
    
    void add_edge(int from,int to,int w)
    {
        e[++tot].nxt=head[from];e[tot].to=to;
        e[tot].w=w;head[from]=tot;
    }
    
    void dfs(int x,int anc) //求直径 
    {
        for(int i=head[x];i;i=e[i].nxt)
        {
            if(e[i].to==anc) continue;
            f[e[i].to]=x;dist[e[i].to]=dist[x]+e[i].w;
            dfs(e[i].to,x);
        }
    }
    
    void find_max(int x,int anc) //计算非直径上的最远距离 
    {
        if(dist[x]>cur) cur=dist[x];
        for(int i=head[x];i;i=e[i].nxt)
        {
            if(e[i].to==anc || on_dia[e[i].to]) continue;
            dist[e[i].to]=dist[x]+e[i].w;
            find_max(e[i].to,x);
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int x,y;ll z;scanf("%d%d%lld",&x,&y,&z);
            add_edge(x,y,z);add_edge(y,x,z);
        }
        
        dfs(1,0);
        for(int i=1;i<=n;i++) if(dist[i]>dist[rt1]) rt1=i;
        dist[rt1]=0;dfs(rt1,0);
        for(int i=1;i<=n;i++) if(dist[i]>dist[rt2]) rt2=i;
        
        on_dia[rt1]=true;dia=dist[rt2];
        for(int i=rt2;i!=rt1;i=f[i])
            on_dia[i]=true;
        printf("%lld
    ",dia);
        
        int l=rt1,r=rt2;
        for(int i=f[rt2];i!=rt1;i=f[i])
        {
            ll pl=dist[i],pr=dia-dist[i];
            dist[i]=cur=0;find_max(i,0);
            if(cur==pl) {l=i;break;} //如果左端有新路径则直接退出 
            if(cur==pr) r=i;
        }
        
        int res=0;
        for(int i=r;i!=l;i=f[i])
            res++;
        printf("%d",res);
        return 0;
    }

    Review:

     1、$dfs$时的代码技巧

    如果递归结束后不好确定经过哪些点,最好在递归过程中更新结果

    2、推结论

    多用反证法和假设法检验某种情况是否可能出现

    尽可能利用结论进行剪枝

  • 相关阅读:
    【20211112】学习,为自己,也为别人
    【20211109】计划会使人拥有魔力
    【20211110】责任是不确性的克星
    【20211113】因材施教
    【20211119】责任能使人变得难以置信
    【20211115】连岳摘抄
    【20211106】连岳摘抄
    c#中用正则过滤所有标点符号
    带农历的JavaScript日期时间
    JQuery常用方法一览
  • 原文地址:https://www.cnblogs.com/newera/p/9247037.html
Copyright © 2011-2022 走看看