疫情控制 blockad
题目描述
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;
对于 60%的数据,2 ≤ n≤1000,0<w <10;
对于 80%的数据,2 ≤ n≤10,000;
对于 100%的数据,2≤m≤n≤50,000,0<w <10。
来源
solution
容易发现答案具有单调性。
二分答案,然后尝试构造最优方案。
我们应该让所有点尽可能往上跳,如果步数充裕还可以去到别的子树去驻扎。
可以用倍增的方法算出每个点会跳到哪里,如果能跳到根就记下还能走几步。
那么跳不到根的,他的贡献是确定的,所以可以先确定根的那些儿子是已被处理好了。
然后我们把剩余的点按剩余步数排序。记剩余步数为res
如果一个点跳不回自己来的儿子,并且这个儿子还没处理好,那就让他跳回去。
因为让另一个res>它的点去肯定不优。
接下来就对每一个还未被占领的儿子按占领代价(也就是根到它的长度)排序。
贪心匹配即可。
总结:先找性质,二分完构造最优解。细节需想清楚。
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 50004
#define ll long long
using namespace std;
int n,m,head[maxn],tot,f[maxn][],be[maxn];
int match[maxn],top;
ll l[maxn][],len[maxn];
bool flag[maxn];
struct node{
int v,nex,w;
}e[maxn*];
struct no{
int st,ed;
ll res;
}s[maxn];
void lj(int t1,int t2,int t3){
e[++tot].v=t2;e[tot].w=t3;e[tot].nex=head[t1];head[t1]=tot;
}
void dfs(int k,int fa){
f[k][]=fa;
if(fa==)be[k]=k;
else be[k]=be[fa];
for(int i=head[k];i;i=e[i].nex){
if(e[i].v!=fa){
l[e[i].v][]=e[i].w;
dfs(e[i].v,k);
}
}
}
void work(int k){
bool F=,fl=;
for(int i=head[k];i;i=e[i].nex){
if(e[i].v==f[k][])continue;
work(e[i].v);
if(!fl)fl=,F=;
F&=flag[e[i].v];
}
flag[k]|=F;
}
bool Mr(no a,no b){
return a.res<b.res;
}
bool cmp(int a,int b){
return len[a]<len[b];
}
bool pd(ll mid){ for(int i=;i<=n;i++)flag[i]=;
for(int i=;i<=m;i++){
int k=s[i].st;ll la=mid;
for(int x=;x>=;x--){
if(f[k][x]&&l[k][x]<=la){
la-=l[k][x],k=f[k][x];
}
}
s[i].ed=k;s[i].res=la;
if(k>)flag[k]=;
}
work();
sort(s+,s+m+,Mr);
for(int i=;i<=m;i++){
if(s[i].ed!=||flag[be[s[i].st]])continue;
int b=be[s[i].st];
if(s[i].res<len[b])flag[b]=,s[i].ed=b;
}
top=;
for(int i=head[];i;i=e[i].nex){
if(flag[e[i].v])continue;
match[++top]=e[i].v;
}
sort(match+,match+top+,cmp);
int j=;
for(int i=;i<=top;i++){ int L=len[match[i]];
while((s[j].ed!=||s[j].res<L)&&j<=m)j++;
j++;
if(j>m+)return ;
}
return ;
}
int main(){
cin>>n;
for(int i=,t1,t2,t3;i<n;i++){
scanf("%d%d%d",&t1,&t2,&t3);
lj(t1,t2,t3);lj(t2,t1,t3);
}
dfs(,);
for(int i=head[];i;i=e[i].nex)len[e[i].v]=e[i].w;
for(int j=;j<=;j++)
for(int i=;i<=n;i++){
f[i][j]=f[f[i][j-]][j-];
l[i][j]=l[i][j-]+l[f[i][j-]][j-];
}
cin>>m;
for(int i=;i<=m;i++)scanf("%d",&s[i].st);
ll l=,r=1e16;
while(l<r){
ll mid=l+r>>;
if(pd(mid))r=mid;
else l=mid+;
}
if(l==1e16)puts("-1");
else cout<<l<<endl;
return ;
}