题解 P1084 【疫情控制】
题目链接
这题思维难度较高 我觉得是好题 故写下此题解 顺便加深对此题的理解
0.特别说明
本篇题解中“堵住”是指某一结点上有军队或者它的子结点被“堵住”的状况。
“堵住”整棵树则是根节点被堵住的状况。
做此题前需要先掌握:贪心 二分答案 倍增 基本图论知识
1.题目分析
(1 解题思路
这题中求移动时间最长的军队的最短军队 所以我们果断用二分答案。至于
(2 有关check函数
如何判断在
跳完之后,军队可以分
我们处理完军队之后,可以看一下当只有第
当第
(3 如何判断-1
很简单,你只需要看一下深度为2的点是否超过
2.坑点
对于有的第二种军队,它直接跳到根结点的子结点就好了。(前提是子结点没有被堵住) 至于如何解决这个问题,代码第93行有写。
此题码量120行左右但是我写出了140行,粗心一点的话bug会很多....
3.代码
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=50005;
struct edge
{
int next,to,val;
}e[N*2];
struct take
{
int pos,rest;
}a[N],b[N];
int f[N][21],cnt,dis[N][21],head[N],army[N],m,used[N],vis[N],dep[N];
void add(int x,int y,int z)
{
e[++cnt].to=y;
e[cnt].val=z;
e[cnt].next=head[x];
head[x]=cnt;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;
for(int i=1;i<=20;++i)
{
f[x][i]=f[f[x][i-1]][i-1];
dis[x][i]=dis[f[x][i-1]][i-1]+dis[x][i-1];
}
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(v!=fa)
{
f[v][0]=x; dis[v][0]=e[i].val;
dfs(v,x);
}
}
}
void look(int x,int fa)
{
int flag=1;
if(used[x]) return;
for(int i=head[x];i;i=e[i].next)
{
int v=e[i].to;
if(v!=fa)
{
flag=0;
look(v,x);
if(!used[v]) return;
}
}
if(!flag) used[x]=1;
}
bool cmp(take x,take y)
{
return x.rest<y.rest;
}
bool check(int x)
{
memset(used,0,sizeof(used));
memset(vis,0,sizeof(vis));
int sizea=0,sizeb=0;
for(int i=1;i<=m;++i) //用倍增跳
{
int p=army[i],cost=0;
for(int j=20;j>=0;--j)
if(cost+dis[p][j]<=x && f[p][j]>1)
{
cost+=dis[p][j];
p=f[p][j];
}
if(f[p][0]==1 && cost+dis[p][0]<=x) //第二种军队
{
a[++sizea].pos=p;
a[sizea].rest=x-cost-dis[p][0];
}
else used[p]=1;
}
sort(a+1,a+sizea+1,cmp);
for(int i=head[1];i;i=e[i].next)
{
look(e[i].to,1);
if(!used[e[i].to])
{
b[++sizeb].pos=e[i].to;
b[sizeb].rest=e[i].val;
}
}
sort(b+1,b+sizeb+1,cmp);//贪心策略
int j=1;
for(int i=1;i<=sizea;++i)
{
if(!vis[a[i].pos] && !used[a[i].pos]) vis[a[i].pos]=1; //第93行代码 直接跳到子结点上
else if(a[i].rest>=b[j].rest) vis[b[j].pos]=1;
while(j<=sizeb && vis[b[j].pos])
++j;
if(j>sizeb) return 1;
}
return 0;
}
int read()
{
int x=0; char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x;
}
int main()
{
int n,l=0,r=0;
n=read();
for(int i=1;i<n;++i)
{
int x,y,z;
x=read(); y=read(); z=read();
add(x,y,z); add(y,x,z);
r+=z;
}
dfs(1,0);
m=read();
for(int i=1;i<=m;++i)
army[i]=read();
int ans=0,sum=0;
for(int i=1;i<=n;++i)
if(dep[i]==2) ++sum;
if(sum>m)
{
cout<<-1;
return 0;
}
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))
{
r=mid-1; ans=mid;
}
else l=mid+1;
}
cout<<ans;
return 0;
}