洛谷 P1084 疫情控制 【二分+数据结构】
题目:
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,
也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境
城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境
城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,
首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在
一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等
于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
分析:
首先,我们发现,对于给定的时间,每一个军队能不能到就是一定的了,那么我们自然可以想到二分答案,对于一个时间,能不能控制疫情呢?因为是一棵树,所以每一个军队越往上走越有利于控制整个国家,对于能到达首都的军队我们就让他到达首都,并且按照需要把它分配给首都底下的直辖市,进而控制另一个没有军队或是军队到不了的子树。具体实现看代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define maxn 50010 using namespace std; struct node { int to; int next; int w; }edge[maxn*2]; struct point { int gra; int v; }extra[maxn],defic[maxn]; int head[maxn*2],arm[maxn],fath[maxn],gran[maxn],dis[maxn],cal[maxn],tim[maxn],b[maxn]; int n,m,tot=0,cnt=0,sum=0,na=0,nc=0; bool comp(const point &a,const point &b) { return a.v>b.v; } void add(int,int,int);//建图 void dfs(int);//处理出要被覆盖的点和已被覆盖的点 void cal_tim(int);//处理出每个军队的能力,能到,不能到 void cal_extra(int);//能到的还有多少剩余 void cal_defic();//不能到的还差多少 bool remedy(); //能到的能不能补齐不能到的 bool ok(int);//二分的时间是否够(其实就是调用以上四个函数) void conn_anc(int);//处理出各个城市所属的直辖市,根节点的儿子 int erfen();//二分时间 int main() { cin>>n; memset(head,0,sizeof(head)); for(int i=1;i<n;i++) { int x,y,ww; cin>>x>>y>>ww; add(x,y,ww); add(y,x,ww); if(x==1 || y==1) tot++; } cin>>m; if(tot>m) { cout<<-1; return 0; } conn_anc(1); for(int i=1;i<=m;i++) cin>>arm[i]; sort(arm+1,arm+1+m); cout<<erfen(); return 0; } void add(int u,int v,int ww) { cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; edge[cnt].w=ww; head[u]=cnt; } void conn_anc(int x) { for(int i=head[x];i;i=edge[i].next) { int vv=edge[i].to; if(fath[x]!=vv) { if(x==1) gran[vv]=vv; else gran[vv]=gran[x]; fath[vv]=x; dis[vv]=dis[x]+edge[i].w; conn_anc(vv); } } } void dfs(int root) { int maxx=-1,now1=0,now2=0; for(int i=head[root];i;i=edge[i].next) { int vv=edge[i].to; if(fath[root]!=vv) { dfs(vv); if(tim[vv]==-1) now2=1;//该点尚未被覆盖 if(tim[vv]>=edge[i].w) now1=1;//该点可以被覆盖 maxx=max(maxx,tim[vv]-edge[i].w); } } if(root!=1&&edge[head[root]].next) { if(now1) tim[root]=max(tim[root],maxx); else if(now2) tim[root]=max(tim[root],-1); else tim[root]=max(tim[root],0); } } void cal_tim(int ti) { for(int i=1;i<=m;i++) if(dis[arm[i]]>=ti) tim[arm[i]]=ti; dfs(1); } void cal_extra(int ti) { for(int i=1;i<=m;i++) { if(dis[arm[i]]<ti) { extra[++na].gra=gran[arm[i]]; extra[na].v=ti-dis[arm[i]]; } } sort(extra+1,extra+na+1,comp); for(int i=1;i<=na;i++) { if(!cal[extra[i].gra]) cal[extra[i].gra]=i; else if(extra[cal[extra[i].gra]].v>extra[i].v) cal[extra[i].gra]=i; } } void cal_defic() { for(int i=head[1];i;i=edge[i].next) { int vv=edge[i].to; if(tim[vv]==-1) { defic[++nc].gra=vv; defic[nc].v=edge[i].w; } } sort(defic+1,defic+nc+1,comp); } bool remedy() { if(na<nc) return false; memset(b,0,sizeof(b)); int i=1,j=1; for(;i<=nc;i++) { if(!b[cal[defic[i].gra]] && cal[defic[i].gra]) b[cal[defic[i].gra]]=1; else { while(j<=na && b[j]) j++; if(j>na || extra[j].v<defic[i].v) return false; b[j]=1,j++; } } return true; } bool ok(int ti) { memset(cal,0,sizeof(cal)); na=nc=0; memset(tim,-1,sizeof(tim)); cal_tim(ti); cal_extra(ti); cal_defic(); return remedy(); } int erfen() { int l=-1,r=999999999; while(l+1<r) { int mid=(l+r)/2; if(ok(mid)) r=mid; else l=mid; } return r; }