疫情控制
给定一棵树 给定一些障碍,用这些障碍来切断根节点到所有叶子节点的路径,可以移动障碍,求总时间最少
可能会发生这样的一种情况: 当前子树的军队去管辖别的子树 当前子树被别的军队管辖
正确且简便的思路应当是这样的:
1.二分答案 在二分的时间内可以分为到达根的节点和没有到达根的节点
2.所有能够到达根的节点排序后去选择尚未被控制的子树
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define mp make_pair
#define fs first
#define sc second
using namespace std;
const int N=5e4+10;
const int M=N*2;
const int INF=0x3f3f3f3f;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-') f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
struct Edge
{
int to,next,w;
}e[M];
int head[N],cnt;
void _add(int a,int b,int c){ e[++cnt]=(Edge){b,head[a],c}; head[a]=cnt;}
void add(int a,int b,int c){ _add(a,b,c); _add(b,a,c); }
int n,m;
int army[N],bel[N],tot,tim[N],isarmy[N];
pair<ll,int> p[N];
ll dep[N],l,r,d[N];
bool v[N],iscon[N];
void dfs(int x,int fa,int t)
{
bel[x]=t;
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(y==fa) continue;
dep[y]=dep[x]+e[i].w;
dfs(y,x,t);
}
}
ll T(int x){ return dep[army[x]]-dep[p[bel[army[x]]].sc];}
bool cmp(int x,int y){return T(x)+p[bel[army[x]]].fs>T(y)+p[bel[army[y]]].fs;}
void trycon(int x,int ff,ll mid)
{
d[x]=INF; bool flag=0,tmp=1;
if(isarmy[x]){d[x]=0; iscon[x]=1; return ;}
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(y==ff) continue;
flag=1;
trycon(y,x,mid);
d[x]=min(d[x],d[y]+e[i].w); tmp&=iscon[y];
}
if(d[x]<=mid || (flag&&tmp) ) { iscon[x]=1; return ;}
}
bool check(ll mid)
{
memset(v,0,sizeof v);
memset(iscon,0,sizeof iscon);
for(int i=1;i<=m;i++)
if(T(i)<=mid) v[i]=1,isarmy[army[i]]--;
for(int i=1;i<=tot;i++)
trycon(p[i].sc,1,mid);
int t1=1,t2=1;
while(t1<=m&&t2<=tot)
{
int t=bel[army[tim[t1]]];
if(iscon[p[t2].sc]) t2++;
else if(!v[tim[t1]]) t1++;
else if(mid-T(tim[t1])<p[t].fs+p[t2].fs) t1++,iscon[p[t].sc]=1;
else iscon[p[t2++].sc]=1, t1++;
}
for(int i=1;i<=m;i++)
if(v[i]) isarmy[army[i]]++;
for(int i=1;i<=tot;i++)
if(!iscon[p[i].sc]) return false;
return true;
}
int main()
{
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read(),z=read(); r+=z;
if(x==1||y==1) p[++tot]=mp(z,x+y-1);
add(x,y,z);
}
m=read();
if(m<tot){ puts("-1"); return 0;}
for(int i=1;i<=m;i++) army[i]=read(),tim[i]=i,isarmy[army[i]]++;
sort(p+1,p+tot+1);
for(int i=1;i<=tot;i++)
{
int x=p[i].sc; dep[x]=p[i].fs;
dfs(x,1,i);
}
sort(tim+1,tim+m+1,cmp);
while(l+1<r)
{
ll mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid;
}
printf("%lld",r);
return 0;
}
//p: 第i个子树的节点编号,距离(从小到大)
//army: 第i个军队的位置
//tim: 所需要时间从大到小排名为i的军队编号
//T: 第i号军队所需要的时间
//bel:每个节点属于哪一个子树
诸多技巧:
- 树上问题的叶子结点要特别注意是不是需要特判
- 映射已经映射的值
- 考虑到0这一种特殊的情况
- while中嵌套else if

浙公网安备 33010602011771号