洛谷 P1084 [NOIP2012 提高组] 疫情控制
题目链接: P1084 [NOIP2012 提高组] 疫情控制
题目大意:
有一个大小为 \(n\) 的树形国家,首都为节点1,其首都出现了一种传染病 COVID-19,为了防止疾病传播到边境,国家派出 \(m\) 个军队部署防疫站,初始每个军队所在城市为 \(P_i\) ,要求军队移动到一些城市,满足在首都到任意一个边境城市的路径上都至少有一个防疫站(其中首都不能建防疫站),求所需的最短时间。
\(1\leq n,m\leq 50000\) ,(我也不知道咋转换题意了,就这样吧)
思路:
想一想就可以发现军队肯定往根节点走是最优的,由于答案具有单调性,考虑二分答案 \(mid\) ,对于走不到根节点的军队,我们尽量让他们往上走即可,主要关注可以走到首都的部队,他们若要控制道路那么肯定都是部署在首都周围的几个城市里,我们拿一个集合表示所有靠走不到根节点的军队无法全部覆盖的周边城市,这时我们还剩有能力跨越根节点的军队可以使用,这时有一个贪心策略:
根据离根节点距离从大到小枚举的每个儿子(从小到大也可以,内部的顺序也换一下即可),如果子树中有军队可用,就选最远的部署在自己这里,如果没有,便选所有部队中离根节点最近且还没被使用的拿过来(更需要这些军队的城市之前已经处理过了)。
最终如果可以覆盖全国,便把 \(mid\) 向下二分,vice versa.
很多题解在将军队往上跳的时候采用的是倍增的方法,是 \(O(nlognlognz)\) 的(\(z\) 为边权),仔细思考可以发现我们只需要知道节点覆盖情况,在 \(dfs\) 的过程中维护最近的军队即可,这样时间复杂度 \(O(nlognz)\) ,可以吊打网上大多数代码(我的开O2是洛谷rank12,数人头rank4)
Code:
不知道为什么这道题写的我身心俱疲,可能状态不好吧。
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 50100
#define ll long long
#define Inf 0x3f3f3f3f3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int head[N],to[N*2],nxt[N*2];
bool ocp[N],used[N];
ll cnt,len[N*2],dis[N],lim;
int city[N],son[N],from[N],mlt[N];
int n,m;
void init(){
mem(head,-1), cnt=-1;
}
void add_e(int a,int b,ll l,bool id){
nxt[++cnt]=head[a];
head[a]=cnt;
to[cnt]=b;
len[cnt]=l;
if(id)add_e(b,a,l,0);
}
void dfs1(int x,int fa){
if(fa==1)from[x]=x;
else from[x]=from[fa];
lim=max(lim,dis[x]);
if(to[head[x]]==fa)head[x]=nxt[head[x]];
for(int i=head[x];~i;i=nxt[i]){
if(to[i]==fa)continue;
dis[to[i]]=dis[x]+len[i];
dfs1(to[i],x);
}
}
ll dfs2(int x,int fa,int mid){
if(ocp[x])return 0;
if(head[x]==-1)return Inf;
ll up=Inf;
ocp[x]=true;
for(int i=head[x];~i;i=nxt[i]){
if(to[i]==fa)continue;
up=min(up,len[i]+dfs2(to[i],x,mid));
ocp[x]&=ocp[to[i]];
}
ocp[x]|=(up<=mid);
return up;
}
bool cmp(int a,int b){
return dis[a]>dis[b];
}
bool check(int mid){
mem(ocp,false);
mem(mlt,-1);
mem(used,false);
int i,p,u;
for(i=0;i<m&&dis[city[i]]>mid;i++)
ocp[city[i]]=true;
dfs2(1,0,mid);
for(p=i;i<m;i++)
if(mlt[from[city[i]]]==-1)mlt[from[city[i]]]=i;
u=m;
for(i=0;i<cnt;i++){
int c=son[i];
if(ocp[c])continue;
if(mlt[c]==-1||used[mlt[c]]){
for(--u;u>=p&&(dis[city[u]]+dis[c]>mid||used[u]);u--);
if(u<p)break;
used[u]=true;
}
else used[mlt[c]]=true;
}
return u>=p;
}
int main(){
ios::sync_with_stdio(false);
int u,v,w;
cin>>n;
init();
for(int i=1;i<n;i++){
cin>>u>>v>>w;
add_e(u,v,w,1);
}
cin>>m;
for(int i=0;i<m;i++)cin>>city[i];
dfs1(1,0);
cnt=0;
for(int i=head[1];~i;i=nxt[i]) son[cnt++]=to[i];
if(cnt>m)return cout<<"-1\n",0;
sort(city,city+m,cmp);
sort(son,son+cnt,cmp);
ll r=lim+dis[son[0]],l=0,mid;
while(l<r){
mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}