noip2018 Day1T3トラック建設

質問の意味:

\(C \)市は、レースのシリーズを開催します。試合前、私たちは街に構築する必要があります\(M \)トラックを。

\(C \)市、これらの接合部は番号が付けられているn接合の合計\(1、2、...、N \) そこ\(N-1 \)道路トラック上の双方向トラフィックの構築に適したストリップ、各2つの接合部を結ぶ道路。2つの交差道路リンクi番目の数であり、\(a_iを\)\(B_i \) 道路の長さ\(L_iを\) これにより\(N-1 \)パス、任意の交点から出発して、他のすべての交差点に到達することができます。

トラックは、互いに異なる道路の集合である\(E_1、E_2、...、E_k \) 接合部から満たすことができる、道路を通過\(E_1、E_2、...、E_k \) 各道路を介して一度、Uターンは)別の交差点に到達することはできません。各道路のトラックの長さに等しい長さの後で。安全性を確保するために、各道路までの要件はを通じてトラックされるように。

現在、トラックのプログラムの構造は決定されていません。あなたの仕事は、実施形態の回路構成を設計することであるので、トラック長のM最小の最大長の回路構成(即ち、できるだけ大きく最短トラックm個のトラックの長さ)。

データ範囲

https://cdn.luogu.com.cn/upload/pic/43164.png

分析:

最大の最小値が、そうさせている場合のでさんは半分に答えるようにしてみてください。

もちろん、正の解の前に、我々はまだ一部で一部を考えます。

(M = 1 \)\場合、

私たちは、木の直径ことがわかります。

    int ans;
    int dis[maxn];//我们的dis数组中dis[i]求的是从i点到它的叶子节点的最长距离
    void dfs(int u,int fa)
    {
        for(int i=head[u];i;i=ed[i].nxt)
        {
            int v=ed[i].to;
            if(v==fa) continue;
            else
            {
                dfs(v,u);
                ans=max(ans,dis[u]+dis[v]+ed[i].w);
                dis[u]=max(dis[u],dis[v]+ed[i].w);
            }
        }
    }

前記\(ANS \)答え即ち、この場合、直径の長さです。

(b_i = a_iを+ 1 \ \ ) 条件

このとき、木はそれにチェーン、我々の直接バイナリ答えです(ただし、この二分法はされていません)。

(a_iを= 1 \)\場合、

すべてのノードのルートであるため、(1 \)\、これはデイジーチャートであり、我々は順(降順)約行の長さによって各側の右端に入れ、最終的な答えは(W_1 + W_ \ {2 * M}、W_2 + W_ {2 * M-1}、\ cdots、w_m + W_ {M + 1} \) の最小値です。

以下の支店については\(3 \)の場合、

それは、正のソリューションの弱いバージョンで、インターネットは一緒にこの特別いわば、サブの部分を書い問題、および正の解の解決を見ていないようでしたので。

上半期の答え\(K \) それに貢献する回答の連鎖は満たしていなければならないとき

\(val_a> = K \)または\(val_a val_b +> = K \) ここで、\(val_bが\)鎖の付加的な長さです)

その二分法の答え、彼は貪欲に付加価値に渡されたときに我々は判断するたびに\(マルチセット\) 最初のケースを行くために、それがに追加する必要はありません\(マルチセット\)だけで、行くこと答えせ\(1 + \)することができ、後者の場合のために、それは直接に(マルチセット\)\最初探すために\(> = K-now_ {分} \) の数、およびそれら両方から2 (マルチセット\)は\から、この時点で削除すること\(マルチセット\)ので、この時点で保存され、最長でと(彼の父親に渡すことができますチェーンの長さは削除されていません\(マルチセット\)それは父からあるの預金側に)到達することができます。

    multiset<int> s[maxn];
    multiset<int>::iterator it;
    
    int dfs(int u,int fa,int k)
    {
        s[u].clear();
        for(int i=head[u];i;i=ed[i].nxt)
        {
            int v=ed[i].to;
            if(v==fa) continue;
            int val=dfs(v,u,k)+ed[i].w;
            if(val>=k) ++ans;
            else s[u].insert(val);
        }
        int Max=0;
        while(!s[u].empty())
        {
            if(s[u].size()==1) return max(Max,*s[u].begin());
            it=s[u].lower_bound(k-*s[u].begin());
            if(it==s[u].begin()&&s[u].count(*it)==1) it++;//因为此时搜到的最小的满足条件的数
            //就是最小的数且该数在multiset中只有一个也即它本身,所以我们往后再找一个数
            if(it==s[u].end())//此时并没有找到比k-*s[u].begin()更大的值,所以*s[u].begin()这
            {//个值是没办法满足条件的,我们就尝试把它与原先已有的Max取一个max,看它是否能传到上面去
                Max=max(Max,*s[u].begin());//做贡献
                s[u].erase(s[u].find(*s[u].begin()));
            }
            else
            {
                s[u].erase(s[u].find(*it));
                s[u].erase(s[u].find(*s[u].begin()));
            }
        }
        return Max;
    }

次に、あなたが完了することができます。

\(コード:\)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<set>
    using namespace std;
    
    const int maxn=5e4+10;
    
    multiset<int> s[maxn];
    multiset<int>::iterator it;
    
    int dis[maxn],ans;
    
    int head[maxn],tot;
    struct Edge
    {
        int to,nxt,w;
        Edge(){};
        Edge(int to,int nxt,int w):to(to),nxt(nxt),w(w){};
    }ed[maxn<<1];
    void add(int u,int v,int w)
    {
        ed[++tot]=Edge(v,head[u],w);
        head[u]=tot;
        ed[++tot]=Edge(u,head[v],w);
        head[v]=tot;
    }
    
    int cnt;
    
    int dfs(int u,int fa,int k)
    {
        s[u].clear();
        for(int i=head[u];i;i=ed[i].nxt)
        {
            int v=ed[i].to;
            if(v==fa) continue;
            else
            {
                int val=dfs(v,u,k)+ed[i].w;
                if(val>=k) ++cnt;
                else s[u].insert(val);
            }
        }
        int Max=0;
        while(!s[u].empty())
        {
            if(s[u].size()==1) return max(Max,*s[u].begin());
            it=s[u].lower_bound(k-*s[u].begin());
            if(it==s[u].begin()&&s[u].count(*s[u].begin())==1) it++;
            if(it==s[u].end())
            {
                Max=max(Max,*s[u].begin());
                s[u].erase(s[u].find(*s[u].begin()));
            }
            else
            {
                ++cnt;
                s[u].erase(s[u].find(*s[u].begin()));
                s[u].erase(s[u].find(*it));
            }
        }
        return Max;
    }
    
    template<class T>void read(T &x)
    {
        bool f=0;char ch=getchar();x=0;
        for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
        for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
        if(f) x=-x;
    }
    
    void get_the_longest(int u,int fa)
    {
        for(int i=head[u];i;i=ed[i].nxt)
        {
            int v=ed[i].to;
            if(v==fa) continue;
            else
            {
                get_the_longest(v,u);
                ans=max(ans,dis[u]+dis[v]+ed[i].w);
                dis[u]=max(dis[u],dis[v]+ed[i].w);
            }
        }
    }
    
    int main()
    {
        int n,m;
        read(n);read(m);
        for(int i=1;i<n;++i)
        {
            int u,v,w;
            read(u);read(v);read(w);
            add(u,v,w);
        }
        get_the_longest(1,0);
        int l=0,r=ans+1;
        while(l+1<r)
        {
            int mid=(l+r)>>1;
            dfs(1,0,mid);
            if(cnt>=m) l=mid;
            else r=mid;
            cnt=0;
        }
        printf("%d\n",l);
        return 0;
    }

コードは、開いている(O2 \)\わずか、開いていない場合(O2 \)\、その後ます(T \)を\アウト

また、(主に半分の本当のドメインへの回答のための)回答の精度を向上させるために、我々は一般的に理解することは少し難しいかもしれ、二分法の数を書き、コードを見て理解します。

    for(int i=1;i<=100;++i)//i表示的就是二分的次数
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    double ans=(l+r)/2;

おすすめ

転載: www.cnblogs.com/iwillenter-top1/p/11823552.html