题目描述
H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。
【题目分析】
贪心。
【代码】
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
int n,m;
int army[50001];
int h[100001],to[100001],ne[100001],w[100001],en=0;
int f[50001][17],top[50001],have[50001],need[50001],all;
long long needtime[50001];int minpos[50001];
long long mintop[50001];
long long g[50001][17],l,r,dis[50001];
long long last[50001];
void add(int a,int b,int c)
{to[en]=b;ne[en]=h[a];w[en]=c;h[a]=en++;}
void dfs(int k,int tp)
{
top[k]=tp;
for (int i=h[k];i>=0;i=ne[i])
{
if (to[i]!=f[k][0])
{
f[to[i]][0]=k;
g[to[i]][0]=w[i];
dis[to[i]]=dis[k]+w[i];
if (k==1) dfs(to[i],to[i]);
else dfs(to[i],tp);
}
}
}
bool dfs(int k)
{
bool ret=true;
if (have[k])
return true;
if (k==1)
for (int i=h[k];i>=0;i=ne[i])
{
if (!dfs(to[i])) need[++all]=to[i],needtime[all]=w[i];
}
else
{
int flag=0;
for (int i=h[k];i>=0;i=ne[i])
if (to[i]!=f[k][0])
{
flag=1;
if (!dfs(to[i])) ret=false;
}
if (flag==0) return false;
}
return ret;
}
bool cmp1(long long a,long long b){return a>b;}
bool ok(long long mid)
{
int cnt=0;all=0;
memset(have,0,sizeof have);
memset(mintop,-1,sizeof mintop);
memset(minpos,0,sizeof minpos);
for (int i=1;i<=m;++i)
{
if (dis[army[i]]<=mid)
{
last[++cnt]=mid-dis[army[i]];
if (last[cnt]<mintop[top[army[i]]]||mintop[top[army[i]]]==-1)
{
if (mintop[top[army[i]]]!=-1)
{
mintop[top[army[i]]]=min(mintop[top[army[i]]],last[cnt]);
minpos[top[army[i]]]=cnt;
}
else
{
mintop[top[army[i]]]=last[cnt];
minpos[top[army[i]]]=cnt;
}
}
}
else
{
int now=army[i];
long long tmp=mid;
for (int j=16;j>=0;j--)
{
if (tmp>=g[now][j])
{
tmp-=g[now][j];
now=f[now][j];
}
}
have[now]=1;
}
}
dfs(1);
for (int i=1;i<=all;++i)
{
if (mintop[need[i]]!=-1&&last[minpos[need[i]]]<needtime[i])
{
last[minpos[need[i]]]=-1,needtime[i]=-1;
}
}
sort(needtime+1,needtime+all+1,cmp1); while (needtime[all]<=0&&all) all--;
sort(last+1,last+cnt+1,cmp1); while (last[cnt]<=0&&cnt) cnt--;
if (cnt<all) return false;
for (int i=1;i<=all;++i)
if (last[i]<needtime[i])
return false;
return true;
}
int main()
{
memset(h,-1,sizeof h);
scanf("%d",&n);
for (int i=1;i<n;++i)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
scanf("%d",&m);
for (int i=1;i<=m;++i) scanf("%d",&army[i]);
dfs(1,0);
for (int j=1;j<=16;++j)
for (int i=1;i<=n;++i)
{
f[i][j]=f[f[i][j-1]][j-1];
g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1];
}
l=0,r=1e10;
long long ans;
while (l<=r)
{
long long mid=(l+r)/2;
int tmp=ok(mid);
if (tmp>0)
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%lld\n",ans);
}

本文介绍了一个算法问题,通过在树形结构的城市网络中合理部署军队来阻止疫情从首都蔓延到边境城市。利用贪心策略和图论知识,文章提供了一种高效求解方案。

被折叠的 条评论
为什么被折叠?



