luogu1084 疫情控制

http://www.elijahqi.win/archives/1095
題目描述

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 提高組 第二天 第三題

數據可能有點水,我忘了寫-1的情況也就是 當我軍隊數比1號節點的孩子要少的話就是-1

主要還得有個性質需要我們看出來 那就是 隨着時間的增加我們可以控制的邊境城市是遞增的

那麼我們可以用二分答案的方法 確定我們的答案是什麼的

我們首先二分一個答案然後如何驗證呢?

我們求出所有軍隊到1需要的時間 然後規定時間內能到1的軍隊我們讓他在1待命,並且求出他還能剩餘的行動時間 要是規定時間內無法到1的軍隊,就讓它停在那裏,然後做好標記,

後面我們再搜索一遍 確定哪些節點(邊境城市)還沒有軍隊 我們還有一個策略就是 如果這個軍隊上到1號節點 並且他的剩餘時間不足夠返回自己上來的那棵和根連接的子樹的時候就 不如讓他回去

不要上到根節點來

然後貪心的讓剩餘時間最大的去做最長的 這個做最長的就是1號節點到深度比他大一的那個節點就可以了

注意pair的使用要是忘了寫first或者second也不會報錯,但是答案可能會不對…

#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 55000
#define ll long long
#define pa pair<ll,int>
using namespace std;
inline int read(){
	int x=0;char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
	return x;
}
struct node{
	int y,z,next;
}data[N<<1];
int fa[N][18],dep[N],n,m,army[N],b[N],Log[N],h[N],num;
ll dis[N][18];bool mark[N],used[N];
void dfs1(int x,int top){
	for (int i=h[x];i;i=data[i].next){
		int y=data[i].y,z=data[i].z;if(x!=1)b[x]=top;
		if (fa[x][0]==y) continue;
		fa[y][0]=x;dis[y][0]=z;dep[y]=dep[x]+1;
		for (int j=1;j<=Log[dep[y]];++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];
		}
		if (fa[y][0]==1) dfs1(y,y);else dfs1(y,top);
	}
}
void dfs2(int x){
	bool flag=true,son_exist=false;
	for (int i=h[x];i;i=data[i].next){
		int y=data[i].y;
		if (fa[x][0]==y) continue;son_exist=true;
		dfs2(y);if (!mark[y]) flag=false;
	}
	if (flag&&son_exist) mark[x]=true;
}
pa q1[N],q2[N];
inline bool judge(ll tot){
	int top1=0,top2=0;memset(mark,0,sizeof(mark));memset(used,0,sizeof(used));
	for (int i=1;i<=m;++i){
		int x=army[i];ll disx=0;
		for (int j=Log[n];j>=0;--j){
			if (fa[x][j]&&disx+dis[x][j]<=tot){
				disx+=dis[x][j];x=fa[x][j];
			}
		}
		if (x==1) q1[++top1]=make_pair(tot-disx,b[army[i]]);else mark[x]=true;
	}dfs2(1);if (mark[1]) return true;
	for (int i=h[1];i;i=data[i].next){
		int y=data[i].y;if (mark[y]) continue;
		q2[++top2]=make_pair(dis[y][0],y);
	}
	
	sort(q1+1,q1+top1+1);sort(q2+1,q2+top2+1);
	//如果去根了,然後發現自己後繼無人,自己剩餘的時間又不夠回去的乾脆就留在兒子那裏的 
	for (int i=1;i<=top1;++i){
		int y=q1[i].second;ll rest=q1[i].first;
		if (!mark[y]&&rest<dis[y][0]) mark[y]=true,used[i]=true;
	}int q1_pointer=1,q2_pointer=1;
	while (q1_pointer<=top1&&q2_pointer<=top2){
		if (used[q1_pointer]) {q1_pointer++;continue;}
		if (mark[q2[q2_pointer].second]){q2_pointer++;continue;}
		if (q1[q1_pointer].first>=q2[q2_pointer].first) ++q1_pointer,++q2_pointer;else ++q1_pointer;
	}
	while (q2_pointer<=top2&&mark[q2[q2_pointer].second])++q2_pointer;
	if (q2_pointer==top2+1) return true;else return false;
}
int main(){
	freopen("1084.in","r",stdin);
	n=read();
	for (int i=1;i<n;++i){
		int x=read(),y=read(),z=read();
		data[++num].y=y;data[num].next=h[x];h[x]=num;data[num].z=z;
		data[++num].y=x;data[num].next=h[y];h[y]=num;data[num].z=z;
	}Log[0]=-1;dep[0]=0;
	for (int i=1;i<=n;++i) Log[i]=Log[i>>1]+1;
	dfs1(1,0);m=read();
	for (int i=1;i<=m;++i) army[i]=read(); 
	ll l=0,r=500000;
	while (l<=r){
		ll mid=l+r>>1;
		if (judge(mid)) r=mid-1;else l=mid+1;
	}
	printf("%lld",l);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章