题目描述
HH 国有 n n 个城市,这 nn 个城市用 n-1 n−1 条双向道路相互连通构成一棵树, 1 1 号城市是首都,也是树中的根节点。
H H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 HH 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
输入输出格式
输入格式:
第一行一个整数 nn ,表示城市个数。
接下来的 n-1n−1 行,每行 3 3 个整数, u,v,wu,v,w ,每两个整数之间用一个空格隔开,表示从城市 u u 到城市 vv 有一条长为 ww 的道路。数据保证输入的是一棵树,且根节点编号为 11 。
接下来一行一个整数 mm ,表示军队个数。
接下来一行 m m 个整数,每两个整数之间用一个空格隔开,分别表示这 mm 个军队所驻扎的城市的编号。
输出格式:
一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出 -1−1 。
输入输出样例
输入样例#1:
4
1 2 1
1 3 2
3 4 3
2
2 2
输出样例#1:
3
说明
【输入输出样例说明】
第一支军队在 22 号点设立检查点,第二支军队从 22 号点移动到 33 号点设立检查点,所需时间为 33 个小时。
【数据范围】
保证军队不会驻扎在首都。
对于 20%的数据, 2≤ n≤ 102≤n≤10 ;
对于 40%的数据, 2 ≤n≤50,0<w <1052≤n≤50,0<w<105
对于 60%的数据, 2 ≤ n≤1000,0<w <1062≤n≤1000,0<w<106
对于 80%的数据, 2 ≤ n≤10,0002≤n≤10,000 ;
对于 100%的数据, 2≤m≤n≤50,000,0<w <1092≤m≤n≤50,000,0<w<109
从上午开始写,,想出思路后慢慢写,写了1个多小时才写完,然后一直调到下午才A,心力交瘁
倍增+贪心
其实思路并不是很难,但是实现起来的细节。。。
大致思路 :
题目要求我们要用m个点“封死”这棵树
那么显然 一个军队越往上跳控制的点就会越多
然后题目问要“封死”这棵树的话,移动距离最大的点移动的最小距离是多少 ————显然是二分时间
具体做法 :
我们就让每个军队在二分的时间内尽量向上跳,看能不能在给定的时间内跳到节点1
如果能,就先让这个军队跳上去,记录下这个军队到节点1时的剩余时间Rest和它在向上跳的路径中1的儿子Top,便于后来的处理。
如果不能,就让这个军队尽量往上跳,并在终点打上控制标记(毕竟越往上跳控制的节点肯定不会比在原来的点少)
处理完所有的军队后,我们再将所有能蹦到节点1的军队按照Rest进行从小到大的排序
然后如果这个点的Rest已经不能再从1蹦回Top了并且Top还没有被控制,那就让这个点别在节点1呆着了,直接回到Top顺便把Top节点控制了就行啦,因为如果它不回去,肯定还要有别的地方的军队来控制Top,那样显然距离更远。
最后再把所有没有被控制的节点1的儿子(注意: 如果这个点的所有子树的叶子节点被控制了,那这个点也算是被控制了)加入队列,然后再按从大到小排个序。
最后把现在仍在节点1的军队按照Rest从大到小排个序,然后和没被控制的节点一一比对就好辣
具体做法说完了,然后我们观察数据范围,n,m<=50000,暴力向上跳肯定是会T的
所以我们使用倍增进行优化
f[i][j]表示从i这个点跳 2^j 步能跳到的节点
st[i][j]表示从i这个点跳 2^j 步的距离
然后军队向上提的时候就和倍增求LCA类似,这样一来就可以A掉此题
然而,这题细节很多啊啊
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
const int M = 50005 ;
const int N = 20 ;
using namespace std;
inline int read(){
int x=0,w=1; char c=getchar();
while(c>'9'||c<'0'){ if(c=='-') w=-1 ;c=getchar() ;}
while(c>='0'&&c<='9'){ x=x*10+c-'0'; c=getchar() ; }
return x*w;
}
int hea[M],num;
int n,m;
int p[M];
int st[M][N],f[M][N],Ans;
int top[M],tdis[M];
int rnum=0;
int q[M],tail=0;
bool lea[M];
bool vis[M];
bool fs=0;
struct E{
int nex,to;
int dis;
}edge[M<<2];
struct Army{
int Rest;
int Top;
}army[M];
inline bool cmp(int a,int b){ return a>b; }
inline bool cmpmin(Army a,Army b){ return a.Rest<b.Rest; }
inline bool cmpmax(Army a,Army b){ return a.Rest>b.Rest; }
inline void add_edge(int from,int to,int dis){
edge[++num].nex=hea[from];
edge[num].to=to;
edge[num].dis=dis;
hea[from]=num;
}
void Dfs(int u,int father){
for(int i=hea[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==father) continue ;
f[v][0]=u;
st[v][0]=edge[i].dis;
Dfs(v,u);
}
}
void RMQ(){
for(int j=1;j<=18;j++)
for(int i=1;i<=n;i++){
f[i][j]=f[f[i][j-1]][j-1];
st[i][j]=st[i][j-1]+st[f[i][j-1]][j-1];
}
}
void Dfs1(int u,int father,int topf,int dist){
top[u]=topf;
tdis[u]=dist;
bool ft=0;
for(int i=hea[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==father) continue ;
ft=1;
Dfs1(v,u,topf,dist);
}
if(!ft) lea[u]=1;
}
void Dfs2(int u,int father){
if(lea[u]){
fs=1;
return ;
}
for(int i=hea[u];i;i=edge[i].nex){
int v=edge[i].to;
if(v==father) continue ;
else if(vis[v]) continue ;
Dfs2(v,u);
if(fs) return ;
}
}
inline bool Look(int v){
fs=0;
Dfs2(v,f[v][0]);
return fs;
}
inline bool judge(int mid){
memset(vis,0,sizeof(vis));
memset(q,0,sizeof(q));
memset(army,0,sizeof(army));
rnum=0;
tail=0;
for(int i=1;i<=m;i++){
int tim=mid;
int now=p[i];
bool syst=0;
while(1){
for(int j=18;j>=0;j--){
if(f[now][j]&&st[now][j]<=tim){
tim-=st[now][j];
now=f[now][j];
break;
}
if(j==0||now==1){
syst=1;
break;
}
}
if(syst) break;
}
if(now==1){
army[++rnum].Top=top[p[i]];
army[rnum].Rest=tim;
}
else vis[now]=1;
}
sort(army+1,army+m+1,cmpmin);
for(int i=1;i<=m;i++)
if(army[i].Rest<tdis[army[i].Top]){
if(!vis[army[i].Top]&&Look(army[i].Top)){
vis[army[i].Top]=1;
army[i].Rest=-1;
}
}
sort(army+1,army+m+1,cmpmax);
for(int i=hea[1];i;i=edge[i].nex){
int v=edge[i].to;
if(!vis[v]&&Look(v))
q[++tail]=edge[i].dis;
}
sort(q+1,q+tail+1,cmp);
for(int i=1;i<=tail;i++)
if(army[i].Rest<q[i])
return false;
return true;
}
int main(){
n=read();
int R=0;
for(int i=1;i<n;i++){
int u = read(),v = read();
int w = read();
add_edge(u,v,w);
add_edge(v,u,w);
R+=w;
}
Dfs(1,0);
for(int i=hea[1];i;i=edge[i].nex){
int v=edge[i].to;
Dfs1(v,1,v,edge[i].dis);
}
RMQ();
m=read();
for(int i=1;i<=m;i++) p[i]=read();
int tmp=0;
for(int i=hea[1];i;i=edge[i].nex) tmp++;
if(tmp>m){
printf("-1
");
return 0;
}
int L=0;
while(L<=R){
int mid=(L+R)>>1;
if(judge(mid)) Ans=mid,R=mid-1;
else L=mid+1;
}
printf("%d
",Ans);
return 0;
}