疫情控制

题目传送门

思路

我们的方法是:二分+贪心+倍增。

我们可以知道:如果这个时间可以让题意满足,则比它大的时间也可以让题意满足。

所以有单调性,我们就可以二分答案。

c h e c k check check函数怎么打呢?我们要使用贪心:显然我们希望每个军队都停留在深度更小的节点(从而管理更多的路径),所以我们就需要军队向上走。

若一个军队可以走到根节点的子节点,则令其暂时停在根节点的子节点。

否则走到能够走到的深度最小的节点。

可以使用树上倍增进行优化。

对于跳得到根节点的子节点军队,我们有两个选择:

对于跳至根的子节点后仍有剩余时间的军队 s s s,分成两种情况:

1.如果剩余时间不能使其在根节点之间跳一个往返,即剩余时间小于 2 × d i s t ( s , r o o t ) 2×dist(s,root) 2×dist(s,root)的军队,原地驻扎。

2.将符合此条件的所有军队按照剩余时间排序,并将还未控制住疫情的根的子节点按照距根的距离排序,用双指针将军队与城市一一进行匹配。这些军队按照剩余时间从小到大排序,然后和上一步处理出来的这些节点一一进行匹配。这是一个可以证明正确性的贪心策略。若所有未被驻扎的节点都有军队驻扎,则说明当前的时间限制可行;反之则不可行。

考虑这么贪心做的正确性,对于剩余时间不够的军队,如果选择跳过树根去另一个子节点 s ′ s′ s驻扎,则必然 d i s t ( r o o t , s ) > d i s t ( r o o t , s ′ ) dist(root,s)>dist(root,s′) dist(root,s)>dist(root,s),这么做可能会导致需要一个剩余时间足够的军队 s ′ ′ s′′ s从其本来位置跨过根跳至 s s s,花费时间 d i s t ( r o o t , s ′ ′ ) + d i s t ( r o o t , s ) dist(root,s′′)+dist(root,s) dist(root,s)+dist(root,s),而这样做花的时间显然比 s s s原地驻扎, s ′ ′ s′′ s跨过根跳至 s ′ s′ s的情况长,于是贪心策略正确。

当然,也有可能存在一个军队剩余时间不够,但是其子树已被另一个军队控制的情况,那么这个军队就应算入到上文的第二种情况中。

输入
void add_edge(int u,int v,int w) {
    ver[++cnt]=v,edge[cnt]=w,next[cnt]=head[u],head[u]=cnt; }

scanf("%d",&n);
for(int i=1,u,v,w;i<n;i++) {
   
	scanf("%d %d %d",&u,&v,&w);
	add_edge(u,v,w),add_edge(v,u,w);
	sum+=w;
}
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d",&b[i]);

打着水博客的心思写了上来

二分
int l=1,r=sum,ans;
bool flag=
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值