• [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、推结论

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

    尽可能利用结论进行剪枝

  • 相关阅读:
    linux系统日志及其rsyslog服务
    C++
    程序员之---C语言细节18(一些奇怪表达式)
    Spring MVC的简单使用方法
    Android系统开发(4)——Autotools
    大话设计模式C++版——代理模式
    JS获取地址栏并拼接參数
    二叉树的应用(1)--二叉树排序树基本操作
    【LeetCode-面试算法经典-Java实现】【067-Add Binary(二进制加法)】
    Android 实现形态各异的双向側滑菜单 自己定义控件来袭
  • 原文地址:https://www.cnblogs.com/newera/p/9247037.html
Copyright © 2020-2023  润新知