题解 P1084 【疫情控制】
题目链接:Luogu 1084
H 国有
现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在最多一个城市建立检查点。军队移动需要的时间等于经过的道路的长度
数据范围:
Solution
预处理
我们可以发现,节点的深度越小,控制的节点(子树内的节点)越多,所以我们可以贪心地把军队尽量向上提。据此,我们需要把每个点往上的距离预处理,用倍增优化。
二分答案
题目要求的是:军队最大的移动时间的最小值,所以我们可以非常自然地想到二分答案,把问题转化为判定性问题(即是否可以在确定的时间内完成)。
判断合法
根据前面的分析,我们在
- 首先解决军队的上提问题
- 如果一个军队无法到达根节点,那么他最高的位置一定是最优的,让他待在那里就可以了!
- 如果一个军队可以得到根节点,那我们求出他到达后还可以走多少时间
rest 以及到达1 前的儿子id ,以便接下来的处理。
- 如果还有叶子没有被控制,那我们肯定是把军队调到
1 的某个儿子,因此我们需要处理出1 的儿子有多少个需要被控制,记录它们和根节点的距离dist 。 - 最后处理可以到达
1 的军队的去向。- 先将这些军队按照
rest 从小到大排序。如果某一个军队的id 需要被控制,那么就让他不要到达1 而直接待在id 就行了。因为如果他不回去,肯定需要别的军队过来控制这个id ,这样显然花费时间更大。 - 再将这些军队按照
rest 从大到小排序,把需要被控制的点按照dist 从大到小排序。把这两个序列从前往后一一对比就行了!
- 先将这些军队按照
- 是否合法的判断:如果步骤
3 中军队用完了可还有节点需要被控制,那么不合法;否则合法。
时间复杂度:
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=5e4+5,M=1e5+5;
int n,m,tot,a[N],lnk[N],ter[M],nxt[M],val[M],f[N][16];
long long g[N][16];
bool tag[N];
struct data {
long long val;
int id;
bool operator < (const data &rhs) const {
return val<rhs.val;
}
} army[N],rest[N];
void add(int u,int v,int w) {
ter[++tot]=v,nxt[tot]=lnk[u],val[tot]=w,lnk[u]=tot;
}
void dfs(int u,int fa) {
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i];
if(v^fa) f[v][0]=u,g[v][0]=val[i],dfs(v,u);
}
}
void putTag(int u,int fa) {
bool res=1,flg=0;
for(int i=lnk[u];i;i=nxt[i]) {
int v=ter[i];
if(v^fa) putTag(v,u),res&=tag[v],flg=1;
}
if(u!=1&&res&&flg) tag[u]=1;
}
void RMQ() {
for(int j=1;j<=15;++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];
}
}
bool check(long long lim) {
memset(tag,0,sizeof(tag));
int cntArmy=0,cntRest=0;
for(int i=1;i<=m;++i) {
int x=a[i];
long long sum=0;
for(int j=15;~j;--j) if(f[x][j]>1&&sum+g[x][j]<=lim) sum+=g[x][j],x=f[x][j];
if(f[x][0]==1&&sum+g[x][0]<=lim) army[++cntArmy]=data{lim-sum-g[x][0],x};
else tag[x]=1;
}
putTag(1,0);
for(int i=lnk[1];i;i=nxt[i]) {
int v=ter[i];
if(!tag[v]) rest[++cntRest]=data{val[i],v};
}
std::sort(army+1,army+cntArmy+1);
std::sort(rest+1,rest+cntRest+1);
int j=1;
for(int i=1;i<=cntArmy;++i) {
if(!tag[army[i].id]) tag[army[i].id]=1;
else if(army[i].val>=rest[j].val) tag[rest[j].id]=1;
while(tag[rest[j].id]&&j<=cntRest) ++j;
}
return j>cntRest;
}
int main() {
scanf("%d",&n);
long long sum=0;
for(int u,v,w,i=1;i<n;++i) {
scanf("%d%d%d",&u,&v,&w);
add(u,v,w),add(v,u,w),sum+=w;
}
dfs(1,0),RMQ();
scanf("%d",&m);
for(int i=1;i<=m;++i) scanf("%d",&a[i]);
long long l=0,r=sum,ans=-1;
while(l<=r) {
long long mid=(l+r)>>1;
check(mid)?(ans=mid,r=mid-1):l=mid+1;
}
printf("%lld\n",ans);
return 0;
}