P1084 疫情控制 二分+圖論
題目描述
H 國有 n 個城市,這 n 個城市用 n-1 條雙向道路相互連通構成一棵樹,1 號城市是首都,也是樹中的根節點。
H 國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從 首都到邊境城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要註意的是,首都是不能建立檢查點的。
現在,在 H 國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連接的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在 一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等於道路的長度(單位:小時)。
請問最少需要多少個小時才能控制疫情。註意:不同的軍隊可以同時移動。
輸入輸出格式
輸入格式:第一行一個整數 n,表示城市個數。
接下來的 n-1 行,每行 3 個整數,u、v、w,每兩個整數之間用一個空格隔開,表示從城市 u 到城市 v 有一條長為 w 的道路。數據保證輸入的是一棵樹,且根節點編號為 1。
接下來一行一個整數 m,表示軍隊個數。
接下來一行 m 個整數,每兩個整數之間用一個空格隔開,分別表示這 m 個軍隊所駐紮的城市的編號。
輸出格式:共一行,包含一個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出-1。
輸入輸出樣例
輸入樣例#1:4
1 2 1
1 3 2
3 4 3
2
2 2
輸出樣例#1:3
說明
【輸入輸出樣例說明】
第一支軍隊在 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。
NOIP 2012 提高組 第二天 第三題
這題的題解網上到處都有,都講的比較好,但是我覺得這題主要難在代碼實現能力。
畢竟在考場上在時限內想出思路並打出代碼還是有難度的。
代碼:
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #define ll long long #define il inline #define db double using namespace std; il int gi() { int x=0,y=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) y=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) { x=x*10+ch-‘0‘; ch=getchar(); } return x*y; } int n,m; int head[1000045],cnt; struct edge { int next,to,lon; }e[1000045]; il void add(int from,int to,int lon) { e[++cnt].next=head[from]; e[cnt].to=to; e[cnt].lon=lon; head[from]=cnt; } int pre[500045][25]; int dist[500045][25]; int deep[500045]; bool vis[500045]; int P; int fa[500045]; void dfs(int x) { int r=head[x]; while(r!=-1) { int now=e[r].to; if(!vis[now]) { deep[now]=deep[x]+1; if(deep[now]==1) P++; vis[now]=1; pre[now][0]=x; dist[now][0]=e[r].lon; if(x==1) fa[now]=now; else fa[now]=fa[x]; dfs(now); } r=e[r].next; } } int pos[500045]; struct node { int root,rest; }c[500045]; struct tree { int position,juli; }h[500045]; void dfss(int x) { int r=head[x]; bool flag=0,wo=0; while(r!=-1) { int now=e[r].to; if(pre[x][0]!=now) { wo=1;//不是葉節點 if(!vis[now]) flag=1; dfss(now); } r=e[r].next; } if(wo==1&&flag==0&&x!=1)//都沒被感染了並且不是葉節點 vis[x]=1; } bool cmp(node a,node b) { return a.rest<b.rest; } bool cmpp(tree a,tree b) { return a.juli<b.juli; } il bool check(int lon) { memset(vis,0,sizeof(vis)); int nice=0; for(int i=1;i<=m;i++) { int now=pos[i],tot=lon; for(int j=20;j>=0;j--) if(tot>=dist[now][j]&&pre[now][j]!=0) { tot-=dist[now][j]; now=pre[now][j]; } if(now==1) { c[++nice].rest=tot; c[nice].root=fa[pos[i]]; } else vis[now]=1;//標記未被感染 } dfss(1); int num=0; int r=head[1]; while(r!=-1) { int now=e[r].to; h[++num].position=now; h[num].juli=e[r].lon; r=e[r].next; } sort(c+1,c+1+nice,cmp); sort(h+1,h+1+num,cmpp); h[num+1].juli=2e9; int now=1; for(int i=1;i<=nice;i++) { if(vis[c[i].root]==0) vis[c[i].root]=1;//自己的標記掉 else { while(vis[h[now].position]) now++;//沒被感染了 if(c[i].rest>=h[now].juli) vis[h[now].position]=1;//標記未被感染 } while(vis[h[now].position]) now++; } if(now>num) return 1; else return 0; } int main() { freopen("1.in","r",stdin); memset(head,-1,sizeof(head)); n=gi(); int x,y; ll z; for(int i=1;i<n;i++) { x=gi(),y=gi(),z=gi(); add(x,y,z); add(y,x,z); } vis[1]=1; dfs(1); int top=log2(n); for(int i=1;i<=top;i++) for(int j=1;j<=n;j++) { pre[j][i]=pre[pre[j][i-1]][i-1]; dist[j][i]=dist[j][i-1]+dist[pre[j][i-1]][i-1]; } m=gi(); if(P>m) { printf("-1\n"); return 0; } for(int i=1;i<=m;i++) pos[i]=gi(); int l=0,r=500000,mid; int ans; while(l!=r) { mid=(l+r)>>1; if(check(mid)) { ans=mid; r=mid; } else l=mid+1; } printf("%d\n",ans); return 0; }
P1084 疫情控制 二分+圖論