P1084 疫情控制 二分+图论
题目描述
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从 首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在 一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:第一行一个整数 n,表示城市个数。
接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。
接下来一行一个整数 m,表示军队个数。
接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。
输出格式:共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
输入输出样例
4
1 2 1
1 3 2
3 4 3
2
2 2
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; }