最近の共通の祖先合成アルゴリズムのメモ

オリジナルリンク
元のタイトルリンク

タイトル説明

国有H N都市、都市相互接続N N-1双方向パスは、ツリーを構成すると、番号1は、首都であり、ツリーのルートです。

国のH資本は高い感染症の危険性を勃発しました。

首都から各パスの国境都市に少なくとも一つのチェックポイントを持つように、いくつかの都市でチェックポイントを確立するための軍事力を使用することを決めたため、流行の拡大を防止するために、流行を制御するために、(市のリーフノードが表現)国境都市、国境の当局都市はまた、チェックポイントを作成することができます。

しかし、それは資本がチェックポイントを確立できないことを指摘しています。

今、国のいくつかの都市でHが軍隊を駐留している、と市は、より多くの軍隊を駐留することができます。

メートルをサポートする部隊の総数。

軍は道路都市へのリンクと資本以外の都市でチェックポイントを確立するために、そして市内で唯一のチェックポイント間を移動することができます。

別の都市への1つの都市から道路移動後の軍は、道路の長さ(:時間単位)に等しい時間を必要としました。

私はあなたを求める:流行を制御するために必要な最小限に何時間?

注意:別の力が同時に移動することができます。

入力形式

最初の行の整数n、都市の数。

次のn-1三つの整数の行、U、V、スペースで区切られた各二つの整数の間で、Uは都市から都市V Wに表し、wは道路の長さを有し、その入力データを確実にツリーで、ルートノードの数は1です。

整数m次線は、力の数を示します。

m個の整数の次の行は、空白で区切られた2つの整数の間の各々は、それぞれ、これらのm個の軍は、都市の数に駐留します。

出力フォーマット

流行ニーズを制御するための最小時間を表す整数を含む1つのラインの合計。あなたは流行の出力を制御することができない場合は-1。

データ範囲

\(2 \ Mの\ N \ 50000 \)
\(0 <W <10 ^ 9 \)

サンプル入力:

4
1 2 1
1 3 2
3 4 3
2
2 2

出力例:

3

サンプル写真

流行制御2.png

レポートの問題解決

問題の意味を理解します

有一种传染病,叫做延迟快乐. by 秦淮岸灯火阑珊

サイトをAcwing、我々はそれが鮮やかだと思います\(N-1 \)ツリーのノード。

今からルートノード(YXCは時間のライブ表示されます)感染症を遅らせると呼ばれるように幸せ。彼がしますツリー全体に感染します

今持っていることが知られて\(m個\)管理者は、彼らがしたい新しいメンバー(リーフノード)を維持するも、この遅延幸せな感染症に感染しているので、それらは個別にライブの一部の間に駐留されます。

(あなたが各部屋の考えることができることは、ライブノードであり、大規模なライブ部屋の間で多くの小さなライブで構成され、私は知らないのはこのためです。

ライブごとに両者の間に、リンクが異なっているリンクは、存在し、彼らが必要とするデータ使用量が同じではありませんの。

各管理者は、別のスタジオに滞在し始めて。彼らがすることができ、シャトルは、ライブの間、自分の嘘の中で生きることを相互に接続されています。

今、私たちが欲しいのトラフィックが最大の消費管理者の最低限の流れを消費流行は新しいメンバー(リーフノード)体に広がらないように、。

管理者は、ルートノードに駐留することはできません。最高の権威を持って、そして今、彼はまた、幸せに感染して延期されています。他の管理者に感染していないYXC所有者が間YXCライブに達することができないので

IF 防止することができません遅れハッピー感染症を、我々は唯一出力することができます\を(--1 \)

最強広告警視

無知は、我々はまだ素敵なタイトルバーの本来の意味を見て、ああを強制して良い感じ。


解析アルゴリズム

上がるために・貪欲な性質

のは、本格的な目的でこの質問を分析してみましょう、プロパティの膨大な数が含まれています

  1. すべての管理者は、より高度なより良いライブに行く間。

まともな言語は、ということである制御ノードのすべてが、ツリーのルートに移動してみてください

これが理由です。

私たちは、管理者は、彼が直面していることを知っている三の大選択を

アップ行きます進捗状況を作るために、、ダウン行きます満足、移動しないでください。ラウンジチェア

私たちのAcwing管理者は、それらのすべては、彼らは確かに、進捗状況を作るために大きな兄弟上がっていくことになります

しかし、これはなぜですか

管理ノードは、彼が範囲を制御することができ、実際には、彼の息子の木です。

遅延のルートから来るので感染症が満足しています。

そして、各ノードは、実際には、ある彼の息子の唯一のチャンネルに、ツリーのルートノード言うことができる唯一の仲介

だから我々はしているよりも大きく、その後、管理の範囲を複数のサブツリーノードを上がっていきます。

これが私たちの最初の貪欲な性質です。


トラフィックのマルチ単調な性質

あなたは私たちがしていると言うなら裕福な(ほとんどのトラフィックを消費する)管理者は、彼がされたトラフィックを消費する(X- \)\タスクを完了するために。GBを。

その後、我々は、この管理者の与える\(X + 250G \)トラフィック、もちろん、彼がタスクを完了することができます。

私たちが彼に与えた場合でも、\(X-1G \)のトラフィックを、それから彼は、タスクを完了できませんでした。

要するに、要約すると、我々は、得た単調な自然を

我々は答えがXであると言うなら、それから、X 0.233 X + 0.233、タスクを完了することができるようになり、タスクを完了してはなりません。


•2つの人々が自然を分類

私たちは、実際には、すべての管理者は、2つのグループに分けることができる、ことを見出しました。

  1. 到達できるルートノード以下の息子を
  2. あなたは到達できないルートノード以下の息子を

私たちは、管理者の管理者である第2のタイプに属している場合ことがわかった私ができる最善の管理者が、その後、明らかに彼は彼が最も浅いノードの深さを可能に達する可能性が到着しました。

上記の貪欲な性質はそのため。その後、管理の範囲も大きく、我々はマネジメントの適用範囲が大きく優れているようにしたい、複数のサブツリーノードに従って求めることができる上を歩くために、よりを管理者に、より良い、結局、空間に管理します大きいです。

次は、分析に焦点を当て、ファーストクラスの管理者、と呼ばれる学校が予備の容量がある管理者のを、我々は、管理者は、二つの主要な人生の選択肢に直面していることを見つけます。

  1. 遵法、エーカーの木の部分木の独自の三分の一を管理する。つまり不動、変更せずに、まだこのノードにとどまります
  2. 職場を転送して制御することができ、すべてのチャンスをつかむ、このノード自分を残して、ルートを通じて、我々は、他のノード、追加の管理サブツリーに行ってきました。

実際には、我々はまた、他は別の会社に変更するために切り替わり、ジョブは元の会社に固執していることを理解することができます。

現在の問題は、私たちはどうやっているの判断管理者に、遵法、または職場の移転です。

我々は持っている必要があり評価の基礎を

絵をよく見てみましょう。

流行制御.PNG

观察这张图片,我们非常清楚地发现.
\[ dis[1->2]>dis[1->3]>dis[1->4] \\\\ dis[a->b]表示节点a到节点b的距离 \]
假如说现在,\(2\)\(3\)这两个节点上面有管理员,然而节点\(4\)上面没有管理员,需要派遣管理员.

我们到底是派遣哪一个节点上的管理员去比较好呢?
\[ 如果处于2号节点的管理员他还有cnt \quad GB的流量 \\\\ 但是cnt < dis[1->2]+dis[1->2] \]
那么这位管理员,绝对不能够动,现在他处于极其重要的战略位置.

假如说这位管理员转移到其他节点的话,那么一定是转移到\(4\)号节点,因为
\[ dis[2->1]<dis[1->4] \]
这样我们才能够在流量不欠费的前提下,抵达目标节点.

但是我们知道目标节点,其实还可以由\(3\)号节点转移过来的,而且消耗的时间更加少.

那么我们把\(4\)号节点留给其他人,可能性更加多.

それとも私たちは、管理者がなくなっている場合、ルート全体で埋めるために別の管理者が存在しなければならない、この画像は、このノードが、これは明らかに別の管理者が駐留ほど良く、十分ではないではありません理解(4 \)\号ノードそれもかからので、ダウン滞在する、管理者、より優れました、

すべてのすべてで、単語や短いが、我々は次のシナリオを同定しました。

駐留ノードニーズのために、我々は送信ルートノードに近い残留流れを管理するために、少数の管理者(前提は、管理者がこのノードに到達できることです)。

つまり、ノードが小、大容量の管理者を消費管理するために、小容量の管理者、ノードの消費を管理するために行ってきました。


まとめプロセス
  1. ジャンプを掛ける、なるべく早く登っているので、すべてのノード

  2. 半分を分析し、最大時間がかかるを最小限に抑えます。

  3. 貪欲割り当て、それを最大限に活用できるようにします。


コード分​​析

#include <bits/stdc++.h>
using namespace std;
#define mk(a,b) make_pair(a,b)
const int N=50000+200;
int head[N<<1],ver[N<<1],Next[N<<1],edge[N<<1],tot;
int deep[N],fa[N][22],cnt,n,m,a[N],sum;
int tot2,tot3,tot4,t;
long long dis[N][22],Free[N],need2[N];
bool vis[N],okk,need[N];
pair<long long,int> h[N];//可以抵达根节点的节点
struct Edge
{
    inline void init()
    {
        memset(head,0,sizeof(head));
        tot=0;
    }
    inline void add_edge(int a,int b,int c)
    {
        edge[++tot]=b;
        ver[tot]=c;
        Next[tot]=head[a];
        head[a]=tot;
    }
    inline void bfs()
    {
        queue<int> q;
        q.push(1);
        deep[1]=1;
        while(q.size())
        {
            int x=q.front();
            q.pop();
            for(int i=head[x]; i; i=Next[i]) //遍历x的出边
            {
                int y=edge[i];//出边
                if(deep[y])//已经访问过了 ,其实就是父亲节点
                    continue;//若深度小于当前节点,说明是当前节点的父节点
                deep[y]=deep[x]+1;//深度+1,儿子节点是y,父亲节点是x
                fa[y][0]=x,dis[y][0]=ver[i];//DP状态
                for(int j=1; j<=t; j++)
                {
                    fa[y][j]=fa[fa[y][j-1]][j-1];
                    dis[y][j]=dis[y][j-1]+dis[fa[y][j-1]][j-1];
                }//DP
                q.push(y);
            }
        }
    }
    inline bool dfs(int x)
    {
        bool ok=false;//判断是不是叶子节点,false为叶子,true为非叶子节点
        if (vis[x])//已经控制了
            return true;
        for(int i=head[x]; i; i=Next[i])//访问所有的出边
        {
            int y=edge[i];//出边节点
            if (deep[y]<deep[x])//比自己深度小的节点,那就是自己的父亲节点
                continue;
            ok=true;//既然有儿子节点了,那么一定不是叶子节点
            if (!dfs(y))//有叶子节点没有被控制,自己肯定也就不会被控制了
                return false;
        }
        if (!ok)//这个节点是叶子节点,而且没有被控制
            return false;
        return true;//所有叶子节点都被控制
    }
    inline bool check(long long s)
    {
        memset(h,0,sizeof(h));
        memset(need,0,sizeof(need));
        memset(vis,false,sizeof(vis));
        memset(need2,0,sizeof(need2));
        memset(Free,0,sizeof(Free));
        tot2=tot3=tot4=0;
        for(int i=1; i<=m; i++) //分类
        {
            long long ans=0,x=a[i];
            for(int k=t; k>=0; k--) //倍增跳跃
                if (fa[x][k]>1 && ans+dis[x][k]<=s)//找到最高能跳到哪里,且不会跳到根节点
                    ans+=dis[x][k],x=fa[x][k];//累加路径长度,并且跳跃
            if (fa[x][0]==1 && ans+dis[x][0]<=s)//跳到了根节点的儿子节点,有剩余时间
                h[++tot2]=mk(s-(ans+dis[x][0]),x);//剩余时间,当前位置
            else
                vis[x]=true;//位置x已经访问
        }
        for(int i=head[1]; i; i=Next[i])
            if (!dfs(edge[i]))
                need[edge[i]]=true;//开始遍历整棵树,康康有哪些节点没有被控制
        sort(h+1,h+1+tot2);//从小到大排序,剩余时间(第一维)
        for(int i=1; i<=tot2; i++) //空闲节点开始出发
            if (need[h[i].second] && h[i].first<dis[h[i].second][0])//你所在的节点需要控制,你抵达不了根节点,而且返回.
                need[h[i].second]=0;//去除标记,这支军队不动,继续驻守
        //我们之前已经剪掉过一次 详情请见s-(ans+dis[x][0]),所以这一次只要比较一下.就是剪掉两次的意思.
            else//可以调动
                Free[++tot3]=h[i].first;//存储空余时间 ,军队可以调动
        for(int i=head[1]; i; i=Next[i])
            if (need[edge[i]])//这个节点需要控制
                need2[++tot4]=dis[edge[i]][0];//需要控制的时间
        if (tot3<tot4)//军队不够用
            return false;
        sort(Free+1,Free+1+tot3);//军队按照空余时间(能力)从小到大排序
        sort(need2+1,need2+1+tot4);//需要驻守节点按照所需代价从小到大排序(驻守就是控制的意思)
        int i=1,j=1;//i表示需要驻守节点,j表示当前军队
        while(i<=tot4 && j<=tot3)//军队没有用完,节点没有走完
            if (Free[j]>=need2[i])
                i++,j++;
            else
                j++;
        if (i>tot4)//全部都驻守完了
            return true;
        else
            return false;
    }
    inline long long Point(void)
    {
        long long l=1,r=sum,mid=0;//二分边界
        while(l<r)
        {
            mid=l+r>>1;
            if (check(mid))
            {
                r=mid;
                okk=true;
            }
            else
                l=mid+1;
        }
        return r;
    }
} g1;
int main()
{
    g1.init();
    scanf("%d",&n);
    for(int i=1; i<n; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        g1.add_edge(a,b,c);
        g1.add_edge(b,a,c);
        sum+=c;//统计所有边权之和
    }
    scanf("%d",&m);
    for(int i=1; i<=m; i++)
        scanf("%d",&a[i]);
    okk=false;
    t=log2(n)+1;
    g1.bfs();
    long long ans=g1.Point();
    printf("%lld\n",okk?ans:-1);
    return 0;
}

おすすめ

転載: www.cnblogs.com/gzh-red/p/11247112.html