• 【JZOJ3213】【SDOI2013】直径


    ╰( ̄▽ ̄)╭

    小 Q最近学习了一些图论知识。根据课本,有如下定义。
    树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有 N-1 条边。
    路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a,b)表示点 a 和点 b 的路径上各边长度之和。称 dis(a,b)为 a、b 两个节点间的距离。
    直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。
    现在小 Q 想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。
    对于 20%的测试数据:N≤100
    对于 40%的测试数据:N≤1000
    对于 70%的测试数据:N≤100000
    对于 100%的测试数据:2≤N≤200000,所有点的编号都在 1..N 的范围内,边的权值≤10^9。

    (⊙ ▽ ⊙)

    首先必须知道的性质是:
    对于任意两条直径,它们一定会有重叠部分
    反证法:
    如果两条直径没有重叠部分,那么一定可以构造出一条更长的直径。

    然后,在这条性质的基础上,我们扩展得到:
    

    所有直径都有共同的重叠部分,而且满足题目要求的边就是这重叠部分。
    证明:
    a,b,c分别是树的三条直径,且ab=αac=β
    αβ=,那么树就会出现环。


    现在题目求的东西就变得很简单了。
    利用树形动态规划,可以求出f[i],h[i],其中:
    f[i]i的子树中,从i向下走的最长链,并用F[i]记录它的方案数。
    h[i]不在i的子树中,从i向上走的最长链,并用H[i]记录它的方案数。


    显然当一个点if[i]+h[i]所有点中f+h的最大值时,它的父边被F[i]H[i]条直径经过。

    答案就等于所有被最多直径经过的边的数目。


    除此之外,还要特殊判断菊花图的情况。


    时间复杂度为O(n)

    ( ̄~ ̄)

    #include<iostream>
    #include<algorithm>
    #include<stdio.h>
    #include<string.h>
    #include<math.h>
    #define ll long long
    using namespace std;
    const char* fin="jzoj3213.in";
    const char* fout="jzoj3213.out";
    const ll inf=0x7fffffff;
    const ll maxn=200007,maxm=maxn*2;
    ll n,i,j,k,l,ans=0;
    ll fi[maxn],ne[maxm],la[maxm],va[maxm],tot,fa[maxn],Fa[maxn];
    ll f[maxn],F[maxn],g[maxn],G[maxn],h[maxn],H[maxn],dmt,num;
    ll son[maxn],pre[maxn],Pre[maxn],suf[maxn],Suf[maxn],pp[maxn];
    void add_line(ll a,ll b,ll c){
        tot++;
        ne[tot]=fi[a];
        la[tot]=b;
        va[tot]=c;
        fi[a]=tot;
    }
    void dfs(ll v,ll from){
        ll i,j,k;
        fa[v]=from;
        for (k=fi[v];k;k=ne[k])
            if (la[k]!=from){
                dfs(la[k],v);
                if (f[la[k]]+va[k]==f[v]) F[v]+=F[la[k]];
                else if (f[la[k]]+va[k]>f[v]){
                    F[v]=F[la[k]];
                    f[v]=f[la[k]]+va[k];
                }
            }
        if (!F[v]) F[v]=1;
    }
    void geth(ll v,ll from){
        ll i,j,k;
        son[0]=0;
        for (k=fi[v];k;k=ne[k]) if (la[k]!=from) son[++son[0]]=la[k],pp[son[0]]=va[k];
        if (son[0]==1 && v==1) H[v]=1;
        pre[0]=Pre[0]=0;
        for (i=1;i<=son[0];i++){
            if (f[son[i]]+pp[i]>pre[i-1]){
                pre[i]=f[son[i]]+pp[i];
                Pre[i]=F[son[i]];
            }else if (f[son[i]]+pp[i]==pre[i-1]) pre[i]=pre[i-1],Pre[i]=Pre[i-1]+F[son[i]];
            else pre[i]=pre[i-1],Pre[i]=Pre[i-1];
        }
        suf[son[0]+1]=Suf[son[0]+1]=0;
        for (i=son[0];i>0;i--){
            if (f[son[i]]+pp[i]>suf[i+1]){
                suf[i]=f[son[i]]+pp[i];
                Suf[i]=F[son[i]];
            }else if (f[son[i]]+pp[i]==suf[i+1]) suf[i]=suf[i+1],Suf[i]=Suf[i+1]+F[son[i]];
            else suf[i]=suf[i+1],Suf[i]=Suf[i+1];
        }
        for (i=1;i<=son[0];i++){
            ll tmp,tmd;
            if (pre[i-1]>suf[i+1]) tmp=pre[i-1],tmd=Pre[i-1];
            else if (pre[i-1]<suf[i+1]) tmp=suf[i+1],tmd=Suf[i+1];
            else tmp=suf[i+1],tmd=Pre[i-1]+Suf[i+1];
            tmp+=pp[i];
            if (tmp>h[v]+pp[i]) h[son[i]]=tmp,H[son[i]]=tmd;
            else if (tmp<h[v]+pp[i]) h[son[i]]=h[v]+pp[i],H[son[i]]=H[v];
            else h[son[i]]=h[v]+pp[i],H[son[i]]=H[v]+tmd;
        }
        for (k=fi[v];k;k=ne[k]) if (la[k]!=from) geth(la[k],v);
    
    }
    int main(){
        scanf("%lld",&n);
        for (i=1;i<n;i++){
            scanf("%lld%lld%lld",&j,&k,&l);
            add_line(j,k,l);
            add_line(k,j,l);
        }
        dfs(1,0);
        geth(1,0);
        for (i=2;i<=n;i++){
            if (dmt<h[i]+f[i]){
                dmt=max(dmt,h[i]+f[i]);
                num=H[i]*F[i];
            }else if (dmt==h[i]+f[i]) num=max(num,H[i]*F[i]);
        }
        for (i=2;i<=n;i++)
            if (dmt==h[i]+f[i]){
                 if (num==H[i]*F[i]) ans++;
                 if (h[i]==f[i] && F[i]*H[i]>1){
                    ans=0;
                    break;
                 }
            }
        printf("%lld
    %lld",dmt,ans);
        return 0;
    }

    (⊙v⊙)

    一条长度最长的链就是直径。
    对于任意两条直径,它们一定会有重叠部分
    所有直径都有共同的重叠部分。

  • 相关阅读:
    工作杠杆
    AngularJS 自定义指令directive 介绍
    CentOS卸载OpenJDK并安装Sun JDK
    jQuery Datatable 表格插件
    ZTree 使用范例
    jQuery UI 实例 – 切换(Toggle)
    curl 抓取页面信息
    报警平台
    PHP imagechar() 图形验证码 字体太小问题
    Discuz!在线中文分词服务
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714794.html
Copyright © 2020-2023  润新知