1. 程式人生 > >【NOIP2012】 疫情控制

【NOIP2012】 疫情控制

fine 建立 left set max class -- 整數 mar

Description

H 國有 n 個城市,這 n 個城市用 n-1 條雙向道路相互連通構成一棵樹, 1 號城市是首都, 也是樹中的根節點。
H 國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境 城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境 城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要註意的是, 首都是不能建立檢查點的。
現在,在 H 國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連接的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在 一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等 於道路的長度(單位:小時)。
請問最少需要多少個小時才能控制疫情。註意:不同的軍隊可以同時移動。

Input

第一行一個整數 n,表示城市個數。
接下來的 n-1 行,每行 3 個整數,u、v、w,每兩個整數之間用一個空格隔開,表示從 城市 u 到城市 v 有一條長為 w 的道路。數據保證輸入的是一棵樹,且根節點編號為 1。
接下來一行一個整數 m,表示軍隊個數。
接下來一行 m 個整數,每兩個整數之間用一個空格隔開,分別表示這 m 個軍隊所駐紮 的城市的編號。

Output

共一行,包含一個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出-1。

Sample Input

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

Sample Output

3

Hint

樣例說明:
第一支軍隊在 2 號點設立檢查點,第二支軍隊從 2 號點移動到 3 號點設立檢查點,所需 時間為 3 個小時。
【數據範圍】
保證軍隊不會駐紮在首都。
對於 20%的數據,2≤ n≤ 10;
對於 40%的數據,2 ≤n≤50,0<w <10^5;
對於 60%的數據,2 ≤ n≤1000,0<w <10^6;
對於 80%的數據,2 ≤ n≤10,000;
對於 100%的數據,2≤m≤n≤50,000,0<w <10^9

題解: 看了題解,又浪費一道好題。 首先,我們要想對於這道題,我們可以先二分一下,把這個問題轉化成一個可行性問題,然後對於到的了跟的軍隊,記錄一下他剩下可以走的距離,如果一個點不能走到根,那麽越向上,那麽肯定他可以控制的節點就必定越多,所以對於跳不到根節點的軍隊,肯定盡量向上走。把他能走到的最高節點打個標記,dfs或者拓撲檢查一下只用跳不上跟的軍隊還有,對於根,還有多少顆子樹還需要軍隊,然後就可以貪心選了; 就是:我們先把需要軍隊的節點(他的爸爸是根),按照他到根的邊權sort一遍,然後將走到根的軍隊按照剩余時間sort一遍,然後按順序處理一下,顯然當前節點一定是最沒用的節點,要讓他發揮最大的價值就和當前需控制的節點比較,如果能控制就控制,如果不能控制就控制當前他來的那顆子樹。最後看一下能否全部控制就可以了。 就是這樣check的。 然後跳就是倍增跳就可以了。 代碼:
#include<iostream>
#include
<stdio.h> #include<stdlib.h> #include<algorithm> #include<cstring> #define ll long long using namespace std; const int MAXN=50100; int st[MAXN][23];ll dis[MAXN][23]; int place[MAXN],Max[MAXN],zui[MAXN],mark[MAXN];bool b[MAXN],flag=1; int wei[MAXN],bb[MAXN],fr[MAXN]; int hh=0,h=0,markk=0; int n,m,num=0;ll tot=0; struct edge{ int first,next,to,quan; }a[MAXN*2]; struct date{ int from,x; }rest[MAXN]; struct data{ int from,x; }cost[MAXN]; bool cmp1(data x,data y){return x.x<y.x;} bool cmp2(date x,date y){return x.x<y.x;} void cl(){ memset(wei,0,sizeof(wei)); memset(rest,0,sizeof(rest)); memset(st,0,sizeof(st)); memset(dis,0,sizeof(dis)); } void addedge(int from,int to,int quan){ a[++num].to=to,a[num].quan=quan; a[num].next=a[from].first,a[from].first=num; } void dfs1(int now,int fa){ if(fa==1) markk=now; if(markk) wei[now]=markk; for(int i=a[now].first;i;i=a[i].next){ int to=a[i].to,quan=a[i].quan; if(to==fa) continue; st[to][0]=now,dis[to][0]=quan; dfs1(to,now); } } void dfs(int now,int fa,int can){ int son=0; for(int i=a[now].first;i;i=a[i].next){ int to=a[i].to; if(to==fa) continue; son++; if(fa==1&&b[now]) dfs(to,now,1); else if(b[to]) dfs(to,now,1); else dfs(to,now,can); } if(son==0&&b[now]) can=1; if(son==0&&can==0) flag=0; } bool check(ll zong){ int hh=0,h=0; memset(fr,0,sizeof(fr)); memset(bb,0,sizeof(bb)); memset(cost,0,sizeof(cost)); memset(mark,0,sizeof(mark)); memset(b,0,sizeof(b)); memset(rest,0,sizeof(rest)); memset(Max,127,sizeof(Max)); memset(zui,0,sizeof(zui)); for(int hhh=1;hhh<=m;hhh++){ int now=place[hhh]; ll tott=0; for(int j=22;j>=0;j--){ if(tott+dis[now][j]<=zong) tott+=dis[now][j],now=st[now][j]; } if(now==1) { rest[++hh].x=zong-tott; rest[hh].from=wei[place[hhh]]; if(rest[hh].x<Max[wei[place[hhh]]]) Max[wei[place[hhh]]]=rest[hh].x,zui[wei[place[hhh]]]=hhh; } else b[now]=1; } for(int i=a[1].first;i;i=a[i].next){ int to=a[i].to,quan=a[i].quan;flag=1; dfs(to,1,0); if(!flag) cost[++h].x=quan,cost[h].from=to; } int now=1,j=1; sort(cost+1,cost+h+1,cmp1); sort(rest+1,rest+hh+1,cmp2); for(int i=1;i<=h;i++) fr[cost[i].from]=i; for(;j<=h;){ while(bb[j]) j++; if(now>hh) break; if(cost[j].x<=rest[now].x||(rest[now].x<cost[j].x&&rest[now].from==cost[j].from)) j++,now++; else {bb[fr[rest[now].from]]=1;now++;} } if(j>h) return 1; return 0; } int main(){ cl(); scanf("%d",&n); for(int i=1;i<n;i++){ int x,y,z;scanf("%d%d%d",&x,&y,&z); addedge(x,y,z),addedge(y,x,z);tot+=z; } scanf("%d",&m); for(int i=1;i<=m;i++) scanf("%d",&place[i]); dfs1(1,0);st[1][0]=1; for(int j=1;j<=22;j++) for(int i=1;i<=n;i++){ if(i==1) st[i][j]=1; else st[i][j]=st[st[i][j-1]][j-1]; dis[i][j]=dis[i][j-1]+dis[st[i][j-1]][j-1]; } ll l=0,r=tot,ans=1<<30,mid; while(l<=r){ mid=(l+r)/2; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans==1<<30) printf("-1"); else printf("%lld",ans); }

【NOIP2012】 疫情控制