[LOJ2607]【NOIP2012】疫情控制
题意:
题目描述
H 国有n个城市,这n个城市用n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。
H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入格式
第一行一个整数n,表示城市个数。
接下来的n-1行,每行3个整数u,v,w,每两个整数之间用一个空格隔开,表示从城市u到城市v有一条长为w的道路。数据保证输入的是一棵树,且根节点编号为1
接下来一行一个整数m,表示军队个数。
接下来一行m个整数,每两个整数之间用一个空格隔开,分别表示这m个军队所驻扎的城市的编号。
输出格式
共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。
数据范围
洛咕,即官方数据范围:
LOJ:
吐槽:
LOJ这数据……真的天坑我艹
看LOJ数据以为$O(n^2logn)$只有30很虚,结果去看官方数据有60……
然后我$O(nlogn)$的正解不仅要开longlong还被卡常卡成95,在洛咕秒过……
SBLOJ!
题解:
由于军队可以同时移动,所以题目要求的就是使最大值最小,这种最优化问题明显二分答案;
一个结论是军队肯定离根节点越近控制的点越多,即深度越小越优,所以军队选择的策略肯定是向上走;
这样贪心的思路就是让尽量多的军队走到根节点,然后走到那些没有军队的根节点的子节点上,这样子就控制了以那个子节点为根的整个子树;
但是有些军队在时间限制内是走不到根节点的,所以要按照走不走得到根节点分类,如果走不到就留在能走到的最高的点,把这些点设为已被控制,走的到的暂时放在根节点,然后记录这些军队是从根节点的哪个子节点走上来的;
这时可以dfs一遍求出哪些点已经被控制了,注意如果一个点的所有子节点都被控制了那么这个点也算被控制了,然后记录下所有没被控制的根节点的子节点;
按照剩余的时间给所有能到达根节点的军队排序,按照到根节点的距离给没被控制的那些子节点排序,明显剩余时间多军队的去支援到根节点距离远的子节点是最优的;
但是还有个问题,就是有些军队去支援后自己来源的那个子节点反而没有军队去控制;
因此排序时要从小到大,然后优先让每个军队向下回到自己来源的那个子节点(这样做就可以忽略上到根节点又回去的过程,所以时间肯定符合),否则去支援其他子节点,最后判断能否控制所有子节点即可。
这里快速求距离用树上倍增实现。
讲的比较复杂,代码里细节也很多,写的时候要注意。
代码:
(LOJ被卡常95分,不加读入优化90分)
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #include<queue> 7 #define inf 10000000000000000 8 #define eps 1e-9 9 using namespace std; 10 typedef long long ll; 11 struct edge{ 12 int v,w,next; 13 }a[600001]; 14 struct node{ 15 ll v; 16 int id; 17 friend bool operator <(node a,node b){ 18 return a.v<b.v; 19 } 20 }ar[300001],nd[300001]; 21 int n,m,u,v,w,tot=0,cnt=0,_cnt=0,arm[300001],fa[300001][20],head[300001]; 22 ll ans=-1,l,r,sum=0,dis[300001][20]; 23 bool isin[300001]; 24 char buffer[1000010],*hd,*tl; 25 inline char Getchar(){ 26 if(hd==tl){ 27 int len=fread(buffer,1,1000000,stdin); 28 hd=buffer,tl=hd+len; 29 if(hd==tl) 30 return EOF; 31 } 32 return *hd++; 33 } 34 inline int rd(){ 35 register int x=0; 36 char c; 37 do c=Getchar(); 38 while(!isdigit(c)); 39 do{ 40 x=(x<<1)+(x<<3)+(c^48); 41 c=Getchar(); 42 }while(isdigit(c)); 43 return x; 44 } 45 void add(int u,int v,int w){ 46 a[++tot].v=v; 47 a[tot].w=w; 48 a[tot].next=head[u]; 49 head[u]=tot; 50 } 51 void dfs(int u,int ff,int ds){ 52 fa[u][0]=ff; 53 dis[u][0]=ds; 54 for(int i=1;i<=19;i++){ 55 fa[u][i]=fa[fa[u][i-1]][i-1]; 56 dis[u][i]=dis[u][i-1]+dis[fa[u][i-1]][i-1]; 57 } 58 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 59 int v=a[tmp].v; 60 if(v!=ff){ 61 dfs(v,u,a[tmp].w); 62 } 63 } 64 } 65 void dfstf(int u,int fa){ 66 bool t1=true,t2=false; 67 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 68 int v=a[tmp].v; 69 if(v!=fa){ 70 dfstf(v,u); 71 t1&=isin[v]; 72 t2=true; 73 } 74 } 75 if(t1&&t2&&u!=1)isin[u]=true; 76 } 77 bool chk(ll k){ 78 int nw,ret=1; 79 ll d=0; 80 for(int i=1;i<=n;i++)isin[i]=false; 81 cnt=_cnt=0; 82 for(int i=1;i<=m;i++){ 83 nw=arm[i]; 84 d=0; 85 for(int j=19;j>=0;j--){ 86 if(fa[nw][j]&&d+dis[nw][j]<=k){ 87 d+=dis[nw][j]; 88 nw=fa[nw][j]; 89 } 90 } 91 if(nw!=1)isin[nw]=true; 92 else{ 93 int vv=arm[i]; 94 for(int j=19;j>=0;j--){ 95 if(fa[vv][j]>1)vv=fa[vv][j]; 96 } 97 ar[++cnt].v=k-d; 98 ar[cnt].id=vv; 99 } 100 } 101 dfstf(1,0); 102 for(int tmp=head[1];tmp!=-1;tmp=a[tmp].next){ 103 int v=a[tmp].v; 104 if(!isin[v]){ 105 nd[++_cnt].v=a[tmp].w; 106 nd[_cnt].id=v; 107 } 108 } 109 sort(ar+1,ar+cnt+1); 110 sort(nd+1,nd+_cnt+1); 111 nd[_cnt+1].v=inf; 112 for(int i=1;i<=cnt;i++){ 113 if(!isin[ar[i].id])isin[ar[i].id]=true; 114 else if(ar[i].v>=nd[ret].v)isin[nd[ret].id]=true; 115 while(isin[nd[ret].id])ret++; 116 } 117 return ret>_cnt; 118 } 119 int main(){ 120 memset(head,-1,sizeof(head)); 121 //scanf("%d",&n); 122 n=rd(); 123 for(int i=1;i<n;i++){ 124 //scanf("%d%d%d",&u,&v,&w); 125 u=rd(),v=rd(),w=rd(); 126 add(u,v,w); 127 add(v,u,w); 128 sum+=w; 129 } 130 //scanf("%d",&m); 131 m=rd(); 132 for(int i=1;i<=m;i++)arm[i]=rd();//scanf("%d",&arm[i]); 133 l=0,r=sum; 134 dfs(1,0,0); 135 while(l<=r){ 136 ll mid=(l+r)/2; 137 if(chk(mid))ans=mid,r=mid-1; 138 else l=mid+1; 139 } 140 printf("%lld",ans); 141 return 0; 142 }