一道树上难题!
首先:时间肯定越长越好,我们可以二分转化为判规定时间是否可行从而简化问题。
然后我们发现,一个节点肯定越靠近根节点其管辖的节点越多,也越优。
在规定时间内,所有节点尽量往根节点靠近。
执行完靠近后出现两类军队:
1:停在根节点的儿子节点。
2:停在非 根节点的儿子节点。
第1类军队涉及到是否移动到根节点再移动到某个根节点的儿子节点。
我们把根节点的儿子节点的集合设为S。
先不考虑第一类节点移动的问题。
只考虑第二类军队。dfs判断只用第二类节点是否能控制住根节点的某些儿子节点。
把S中仍然未被控制的点集设为H。(某个节点被控制的条件:要么它本身存在一个军队,或者其所有儿子节点都已经被控制)
对于点集H。
这时候考虑第一类军队。这些军队既可以留在当前节点控制当前节点,也可以通过根节点到达S中其他节点。
设军队剩余可移动量为res。停留当前节点不花费移动力,而到其他节点花费移动力。
这导致我们不能统一贪心的分配军队。
我们思考发现:
当某个节点x存在一个军队t,其移动力剩余量小于 d[x]*2(d[x]为x节点到根节点的距离),则如果这个节点没被第二类军队控制,则这个节点一定是通过当前节点的第一类军队 进行控制。
否则其余节点y的军队需要花费d[y]+d[x],而其本身的军队到其他节点需要d[x]+d[z],显然需要d[z]<d[x]。
则不如y直接到z优。(因为y剩余的移动力到达的点大于等于 军队t到达的节点 )
而当军队t的移动力大于等于d[x]*2时,其先到达根节点,剩余移动力完全可以再返回x,这类军队可以统一进行分配。
所以我们可以这样贪心:
先把所有第一类军队的res从小到大排序。
如果当前军队t所在节点x尚未被控制,且其行动力小于d[x]*2,则把当前节点控制,把军队t的行动力设为0(相当于固定了当前的军队来控制x点)。
其余军队一律统一的贪心分配即可。(按儿子节点距离从大到小,双指针进行分配即可)
实现细节看代码。
这题思维量还是蛮大的!值得细品
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
const int M = 5e4+7;
int head[M],cnt;
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
int n,m;
int a[M];//在i点有多少个军队
int f[M][18],dep[M];
int fg[M];//i节点所在子树是否被控制住
ll d[M];
void dfs(int x,int fa)
{
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to,w=ee[i].w;
if(y==fa)continue;
dep[y]=dep[x]+1;
d[y]=d[x]+w;
f[y][0]=x;
dfs(y,x);
}
}
void pre()
{
dep[1]=1;dfs(1,0);
for(int i=1;i<=17;i++)for(int j=1;j<=n;j++)f[j][i]=f[f[j][i-1]][i-1];
}
struct node{
int s;ll res;
bool operator < (const node &r)const {
return res<r.res;
}
}p[M];
void gao(int x,int fa)
{
int nm=0,tg=0;
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to;
if(y==fa)continue;
gao(y,x);
nm++;
if(fg[y]==1)tg++;
}
if(nm&&nm==tg)fg[x]=1;//其不是叶子节点,且所有儿子节点都被控制,则这个点也会被控制
}
bool ck(ll T)//能否花不到T的时间控制疫情
{
memset(fg,0,sizeof(fg));
int sz=0;
for(int i=1;i<=m;i++)
{
int x=a[i];
ll res=T;
for(int j=17;j>=0;j--)
if(dep[f[x][j]]>1&&res>=d[x]-d[f[x][j]])res-=d[x]-d[f[x][j]],x=f[x][j];
if(dep[x]==2)p[++sz]=node{x,res};
else fg[x]=1;
}
gao(1,0);
sort(p+1,p+1+sz);
for(int i=1;i<=sz;i++)
{
int x=p[i].s;ll res=p[i].res;
if(!fg[x]&&res<=d[x]*2)fg[x]=1,p[i].res=0;
p[i].res-=d[x];
}
sort(p+1,p+1+sz);
vector<pair<ll,int> >v;
for(int i=head[1];i;i=ee[i].nxt)
{
int y=ee[i].to;
if(fg[y]==0)v.pb({d[y],y});
}
sort(v.begin(),v.end());
bool flag=true;
int tp=sz;
for(int i=v.size()-1;i>=0;i--)
{
ll ds=v[i].first;
if(tp<=0||p[tp].res<ds)
{
flag=false;
break;
}
tp--;
}
if(flag)return true;
return false;
}
int main()
{
cin>>n;
ll l=0,r=0,ans=0;
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
r+=w;
}
cin>>m;
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
pre();
while(l<=r)
{
ll mid=(l+r)/2;
if(ck(mid))ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<endl;
return 0;
}