首页 > 代码库 > 习题:疫情控制(二分+倍增+贪心)

习题:疫情控制(二分+倍增+贪心)

                                                noip2012疫情控制

描述
H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
格式
输入格式

第一行一个整数n,表示城市个数。
接下来的n-1行,每行3个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市u到城市v有一条长为w的道路。数据保证输入的是一棵树,且根节点编号为1。
接下来一行一个整数m,表示军队个数。
接下来一行m个整数,每两个整数之间用一个空格隔开,分别表示这m个军队所驻扎的城市的编号。
输出格式

共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
样例1
样例输入1[复制]

4
1 2 1
1 3 2
3 4 3
2
2 2
样例输出1[复制]

3
限制
每个测试点2s
提示
保证军队不会驻扎在首都。
对于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。 

 

分析:
最大最小显然是二分,我们二分移动的最长时间,如果当前这个最长时间足以让军队控制所有叶节点,那么就减少上界寻找更少的时间,否则增加下界。

如何判断当前这个时间是否能让军队控制所有叶节点则是本题难点
我们可以得到这样一个思路

1.一个军队在树中深度越小,其控制的叶节点要么更多,要么不变,不会减少,所以我们希望一个军队在不超过最长时间的前提下尽量往上升(也就是尽量靠经跟节点)
2.所有军队都上升到所能达到的最小深度的位置,此时能控制的叶节点更多,如果这样都控制不了所有叶节点,说明此时设置的最长时间太小了,应该增加最长时间寻找可行答案。如果这样可以控制所有叶节点,说明是可行的。

不过我们知道,如果这个最长时间很长,有的军队在这段时间可以到达根节点,有的还有剩余,然而根节点是不能放置军队的,所以这些军队要往下降,但降多少,往哪降最优呢
1.显然如果往下降,只降到深度为2的节点是最优的(根节点深度为1)因为这样控制的叶节点最多。
2.选择降到哪个深度为2的节点才是关键,我们先把所有能够上升到根节点的军队忽略掉,这时,如果有某个深度为2的节点i其叶节点中有没被控制的,我们可以派根节点的军队到i去控制。

然而可到达根节点的军队可能很多,未被控制的叶节点也很多,我们需要用贪心的方法进行匹配,用剩余时间最长的去控制离根节点路程最长的深度2的节点,但是如果有一些军队能通过该点到达根节点,则从这些军队中选择剩余时间最少的来控制。预处理后扫一遍就出来了。

军队上升用倍增处理,判断是否控制所有叶节点用一次dfs,总时间效率为
O(m*log(L)*log(n)) L为二分搜索上界

 

技术分享
program blockade;type  point=^node;   node=record     x,v:longint; next:point;   end;  arr=array[0..50000]of longint;var  w:array[0..50000]of point;  f:array[0..30,0..50000]of longint;  mark,flag:array[0..50000]of boolean;  d,s,comefrom,army,a,b,rest,need,minr:array[0..50000]of longint;  n,i,m,maxv,maxd,x,y,v:longint;procedure qsort(l,h:longint; var a,b:arr);var i,j,t,m:longint;begin i:=l; j:=h; m:=a[(i+j) div 2]; repeatwhile a[i]>m do inc(i);while m>a[j] do dec(j);if i<=j then  begin   t:=a[i]; a[i]:=a[j]; a[j]:=t; t:=b[i]; b[i]:=b[j]; b[j]:=t;inc(i); dec(j);  end;  until i>j; if i<h then qsort(i,h,a,b); if j>l then qsort(l,j,a,b);  end;procedure workfirst;var i:longint;begin  fillchar(f,sizeof(f),0); fillchar(s,sizeof(s),0);  maxv:=0; maxd:=0;end;procedure add(x,y,v:longint);var p:point;begin  new(p); p^.x:=y;p^.v:=v; p^.next:=w[x];w[x]:=p;end;procedure dfs1(x,y,deep,v:longint);var p:point; t:longint;begin  new(p); p:=w[x]; f[0,x]:=y; d[x]:=deep;  if x<>1 then comefrom[x]:=v;  while p<>nil do  begin    if p^.x<>y then     begin s[p^.x]:=s[x]+p^.v; if x=1 then t:=p^.x else t:=v; dfs1(p^.x,x,deep+1,t); end;    p:=p^.next;  end;  if d[x]>maxd then maxd:=d[x];end;function dfs2(x:longint):boolean;var p:point;begin  if mark[x]=true then exit(true);  new(p); p:=w[x]; if (p^.x=f[0,x])and(p^.next=nil) then exit(false);  while p<>nil do   begin     if p^.x<>f[0,x] then     if dfs2(p^.x)=false then exit(false); p:=p^.next;   end;  exit(true);end;procedure find;var i,j:longint;begin  for i:=0 to trunc(ln(maxd)/ln(2))-1 do   for j:=0 to n do     if f[i,j]<0 then f[i+1,j]:=-1      else f[i+1,j]:=f[i,f[i,j]];end;function cheak(mid:longint):boolean;var i,j,n1,n2,tot,t:longint; p:point;begin  n1:=0; n2:=0;  fillchar(mark,sizeof(mark),false); fillchar(rest,sizeof(rest),0); rest[0]:=maxlongint;  for i:=1 to m do   begin     x:=army[i]; tot:=0;     for j:=trunc(ln(maxd)/ln(2)) downto 0 do      begin        y:=f[j,x];        if y>0 then        if (tot+s[x]-s[y]<=mid) then begin tot:=tot+s[x]-s[y]; x:=y; end;        if x=1 then break;      end;     if x=1 then begin inc(n1); a[n1]:=i; rest[n1]:=mid-tot; end     else mark[x]:=true;   end;  new(p);p:=w[1];  while p<>nil do   begin     minr[p^.x]:=0;     if dfs2(p^.x)=false then begin    inc(n2); b[n2]:=p^.x; need[n2]:=p^.v;  end; p:=p^.next;   end;  if n2=0 then exit(true);  qsort(1,n1,rest,a); qsort(1,n2,need,b);  for i:=1 to n1 do   begin     y:=comefrom[army[a[i]]]; if rest[i]<rest[minr[y]] then minr[y]:=i;   end;  i:=1;  fillchar(flag,sizeof(flag),true);  for j:=1 to n2 do   begin      y:=b[j]; t:=minr[y];     if (t>0)and(flag[t]=true) then begin flag[t]:=false; end      else        begin          while (flag[i]=false)and(i<=n) do inc(i); if i>n then exit(false);          if rest[i]<need[j] then exit(false) else begin flag[i]:=false; inc(a[i]); end;        end;   end;  exit(true);end;procedure work;var i,l,r,mid,ans:longint;begin  workfirst;  dfs1(1,-1,1,0); for i:=1 to n do begin if s[i]>maxv then maxv:=s[i];  end;  find;  maxv:=maxv*2; l:=1; r:=maxv; ans:=r;  while l<=r do   begin     mid:=(l+r) div 2;     if cheak(mid)=true then begin ans:=mid; r:=mid-1; end       else l:=mid+1;   end;  if l>maxv then ans:=-1;  writeln(ans);end;begin  readln(n);  for i:=1 to n-1 do   begin     readln(x,y,v);     add(x,y,v); add(y,x,v);   end;  readln(m);  for i:=1 to m do    read(army[i]);  work;end.
View Code

 

习题:疫情控制(二分+倍增+贪心)