1. 程式人生 > >[multiset][貪心]NOIP2018 TG T1 賽道修建

[multiset][貪心]NOIP2018 TG T1 賽道修建

題目描述

C 城將要舉辦一系列的賽車比賽。在比賽前,需要在城內修建 m 條賽道。

C 城一共有 nn 個路口,這些路口編號為1,2,,n,有 n1 條適合於修建賽道的雙向通行的道路,每條道路連線著兩個路口。其中,第 i 條道路連線的兩個路口編號為 ai 和 bi,該道路的長度為 li。藉助這 n1 條道路,從任何一個路口出發都能到達其他所有的路口。

一條賽道是一組互不相同的道路 e1,e2,,ek,滿足可以從某個路口出發,依次經過 道路 e1,e2,,ek(每條道路經過一次,不允許調頭)到達另一個路口。一條賽道的長度等於經過的各道路的長度之和。為保證安全,要求每條道路至多被一條賽道經過。

目前賽道修建的方案尚未確定。你的任務是設計一種賽道修建的方案,使得修建的 mm 條賽道中長度最小的賽道長度最大(即 m 條賽道中最短賽道的長度儘可能大)

輸入輸出格式

輸入格式:

 

輸入檔案第一行包含兩個由空格分隔的正整數 n,m,分別表示路口數及需要修建的 賽道數。

接下來 n-1 行,第 ii 行包含三個正整數 ai,bi,li,表示第 i 條適合於修建賽道的道 路連線的兩個路口編號及道路長度。保證任意兩個路口均可通過這 n1 條道路相互到達。每行中相鄰兩數之間均由一個空格分隔。

 

輸出格式:

 

輸出共一行,包含一個整數,表示長度最小的賽道長度的最大值。

 

輸入輸出樣例

輸入樣例#1:
7 1 
1 2 10 
1 3 5 
2 4 9 
2 5 8 
3 6 6 
3 7 7
輸出樣例#1:
31
輸入樣例#2: 
9 3 
1 2 6 
2 3 3 
3 4 5 
4 5 10 
6 2 4 
7 2 9 
8 4 7 
9 4 4
輸出樣例#2:
15

說明

【輸入輸出樣例 1 說明】

所有路口及適合於修建賽道的道路如下圖所示:

道路旁括號內的數字表示道路的編號,非括號內的數字表示道路長度。 需要修建 1 條賽道。可以修建經過第 3,1,2,6 條道路的賽道(從路口 4 到路口 7), 則該賽道的長度為 9 + 10 + 5 + 7 = 31,為所有方案中的最大值。

【輸入輸出樣例 2 說明】

所有路口及適合於修建賽道的道路如下圖所示:

需要修建 3條賽道。可以修建如下 3條賽道:

  1. 經過第 1,6條道路的賽道(從路口 1 到路口7),長度為 6 + 9 = 15
  2. 經過第5,2,3,8 條道路的賽道(從路口6 到路口 9),長度為 4 + 3 + 5 + 4 = 16
  3. 經過第 7,4 條道路的賽道(從路口 8 到路口5),長度為 7 + 10 = 17。 長度最小的賽道長度為 15,為所有方案中的最大值。

【資料規模與約定】

所有測試資料的範圍和特點如下表所示 :

其中,“分支不超過 3”的含義為:每個路口至多有 3 條道路與其相連。 對於所有的資料, 2 ≤ n ≤ 50,000, 1 ≤ m ≤ n-1, 1ai,bin,1li10,000。

 

分析

其實這道題挺簡單的(果然是我太菜了嗎……)

二分長度應該都想得到,畢竟最小值最大,二分性不說了

首先我們可以證明,如果從i出發有兩條鏈,它們的值大於等於length的話,那麼合併它們必定不虧

為什麼呢?因為這其中最多上傳一條,也就是至多貢獻1的答案,那麼顯然合併會更加優秀

那麼我們可以用f[i]表示從i出發向下所得到未被佔用的最長鏈,那麼我們用multiset存所有能夠上傳到當前點的鏈,即f[son]+w[edge]

那麼首先我們先將已經滿足要求的挑出來,答案++

然後我們從小到大列舉鏈,用lowerbound找到最合適的鏈和它匹配掉,如果找不到合適的鏈,那麼可以將這條鏈記錄為f[i](反正鏈長在multiset裡面是單調的)

然後記得清空multiset,還有multiset和儲存不能和子節點一起進行,要遍歷完子節點才能儲存鏈(因為共用一個嘛)

 

#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
const int N=5e4+10;
struct Edge {
    int u,v,w,nx;
}g[2*N];
int cnt,list[N];
int f[N];
multiset<int> t;
int n,m,lans,ans,length;

void Add(int u,int v,int w) {
    g[++cnt]=(Edge){u,v,w,list[u]};list[u]=cnt;
}

void DFS(int u,int fa) {
    for (int i=list[u];i;i=g[i].nx)
        if(g[i].v!=fa) DFS(g[i].v,u);
    for (int i=list[u];i;i=g[i].nx)
        if(g[i].v!=fa) t.insert(g[i].w+f[g[i].v]);
    while (!t.empty()) {
        int ed=*t.rbegin();
        if (ed>=length) lans++,t.erase(t.find(ed));
        else break;
    }
    f[u]=0;
    while (!t.empty()) {
        int bg=*t.begin();t.erase(t.begin());
        multiset<int>::iterator nx=t.lower_bound(length-bg);
        if (nx==t.end()) f[u]=bg;
        else t.erase(nx),lans++;
    }
    t.clear();
}

bool Check(int x) {
    lans=0;length=x;
    DFS(1,0);
    return lans>=m;
}

int main() {
    scanf("%d%d",&n,&m);
    int l=2147483647,r=0;
    for (int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),Add(u,v,w),Add(v,u,w),l=min(l,w),r+=w;
    int mid;
    while (l<=r) {
        mid=l+r>>1;
        if (Check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    printf("%d",ans);
}
View Code