题目大意

  给出一棵n个节点的树,根是1,要在除根节点以外的点建立检查点,使得从每条根到叶子的路径上都至少存在一个检查点。检查点由军队来建立。初始军队的位置是给定的,移动军队走一条边需要花费这条边的权值的时间。现在要求一个方案,移动军队到某个最佳位置,使得总用时最少。

【数据范围】
保证军队不会驻扎在首都。
对于20%的数据,2≤ n≤ 10;
对于40%的数据,2 ≤n≤50,0<w <105
对于60%的数据,2 ≤ n≤1000,0<w <106
对于80%的数据,2 ≤ n≤10,000;
对于100%的数据,2≤m≤n≤50,000,0<w <109

首先确定思路是二分。

然后关键是怎么check

每个军队肯定是要尽量往上走的。把军队分为走得到root和走不到两种情况。

把仅通过走不到root的军队就能控制的第二层的结点去掉,剩下的就需要通过剩下的军队来控制。

每个军队都要匹配一个结点,排序完直接匹配就可以了。但是这里有一个细节,就是如果一个军队要控制自己所属的结点,就不需要再额外添加时间。

于是我们可以先按军队剩余时间从小到大排,再按需要的时间排序没控制的结点,然后决定每一个军队控制哪个结点。由于已经排过序了,所以当前军队肯定是最没用的。所以如果他所属的结点未被控制,就让他去肯定是最好的方案。

还有一个问题。怎么得出那些结点没被控制?这个有几个方法,可以用倍增直接暴力把军队推上去,还有就是用拓扑排序。

这里介绍下拓扑的方法:

  对于每一个结点记录所有军队往上走走到这里剩余时间的最大值time[]。再记一个能否到达arr[],如果time[u] >= 0则arr[u] = true;如果u的所有儿子都可控制,则arr[u] = true;否则都是false

然后问题就解决了!时间复杂度O(n*log2n)

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
typedef long long ll;
const int N = 50000 + 9;
int ec,n,q[N],mark[N],army[N],t1[N],t2[N],ance[N],father[N],son[N],m,times;
ll dis[N],time[N],cost[N];
bool notleaf[N],arr[N],can_use[N];
struct edge{int next,link,w;}es[N*2];
inline bool cmp1(const int i,const int j){return cost[i] < cost[j];}
inline bool cmp2(const int i,const int j){return dis[army[i]] > dis[army[j]];}
inline void addedge(const int x,const int y,const int z)
{
es[++ec].next = son[x];
son[x] = ec;
es[ec].link = y;
es[ec].w = z;
}
inline void Addedge(const int x,const int y,const int z){addedge(x,y,z),addedge(y,x,z);}
bool check(const ll mid)
{
++times;
memset(time,-1,sizeof(*time)*(n + 1));
memcpy(arr,notleaf,sizeof(*arr)*(n + 1));
memset(can_use,0,sizeof(*can_use)*(m + 1));
for (int i = n; i; --i) {
const int u = q[i];
if (mark[u] && dis[u] > mid) time[u] = mid;
if (time[father[u]] < time[u] - cost[u])
time[father[u]] = time[u] - cost[u];
if (time[u] >= 0) arr[u] = true;
if (!arr[u]) arr[father[u]] = false;
}
for (int i = 1; i <= m; ++i)
if (dis[army[i]] <= mid) can_use[i] = true;
t1[0] = t2[0] = 0;
for (int i = son[1]; i; i = es[i].next)
if (!arr[es[i].link]) t1[++t1[0]] = es[i].link;
for (int i = 1; i <= m; ++i)
if (can_use[i]) t2[++t2[0]] = i;
std::sort(t1 + 1,t1 + 1 + t1[0],cmp1);
std::sort(t2 + 1,t2 + 1 + t2[0],cmp2);
for (int i = 1,j = 1; i <= t2[0]; ++i) {
if (!arr[ance[army[t2[i]]]])
arr[ance[army[t2[i]]]] = true;
else if (cost[t1[j]] + dis[army[t2[i]]] <= mid) arr[t1[j]] = true;
while (j <= t1[0] && arr[t1[j]]) ++j;
if (j > t1[0]) return true;
}
return false;
}
void BFS(const int s)
{
int h = 1,t = 0;
q[++t] = s;
while (h <= t) {
const int u = q[h++];
notleaf[u] = false;
for (int i = son[u]; i; i = es[i].next) {
const int v = es[i].link;
if (v == father[u]) continue;
dis[v] = dis[u] + es[i].w;
father[v] = u;
cost[v] = es[i].w;
if (u != s) ance[v] = ance[u];
else ance[v] = v;
q[++t] = v;
notleaf[u] = true;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("blockade.in","r",stdin);
freopen("blockade.out","w",stdout);
#endif
scanf("%d",&n);
ll r = 1,l = 0,tot = 0;
for (int i = 1,x,y,z; i < n; ++i) {
scanf("%d%d%d",&x,&y,&z);
r += z;
Addedge(x,y,z);
}
tot = r;
scanf("%d",&m);
for (int i = 1; i <= m; ++i) {
scanf("%d",army + i);
mark[army[i]] = i;
}
BFS(1);
while (l + 1 < r) {
const ll mid = (l + r)/2;
if (check(mid)) r = mid;
else l = mid;
}
if (r == tot) puts("-1");
else printf("%I64d\n",r);
//check(9);
}

  

noip2012疫情控制 题解的更多相关文章

  1. NOIP2012 疫情控制 题解(LuoguP1084)

    NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...

  2. [NOIp2012]疫情控制 题解

    好久没更,强迫自己写一篇. 神 tm 大预言家出的题 注意到如果 \(x\) 小时可以控制住疫情,则 \(\forall x'>x\) 必然也可以控制住疫情,显然答案具有单调性,可以二分答案. ...

  3. [NOIP2012]疫情控制 贪心 二分

    题面:[NOIP2012]疫情控制 题解: 大体思路很好想,但是有个细节很难想QAQ 首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log. 然后发现一个军队,越往上走肯定可以 ...

  4. Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)

    Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...

  5. luoguP1084 疫情控制(题解)(搜索+贪心)

    luoguP1084 疫情控制 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include& ...

  6. NOIP2012 D2T3 疫情控制 题解

    题面 这道题由于问最大值最小,所以很容易想到二分,但怎么验证并且如何实现是这道题的难点: 首先我们考虑,对于一个军队,尽可能的往根节点走(但一定不到)是最优的: 判断一个军队最远走到哪可以树上倍增来实 ...

  7. noip2012 疫情控制

    [问题描述] H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子 ...

  8. NOIP2012疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

  9. [NOIP2012]疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

随机推荐

  1. JavaScript 字符串操作:substring, substr, slice

    在 JavaScript 中,对于字符串的操作有 substring, substr, slice 等好多个内置函数,这里给大家推荐一篇介绍 substring, substr, slice 三者区别 ...

  2. 【BZOJ】2049: [Sdoi2008]Cave 洞穴勘测 LCT

    [题意]给定n个点和m个操作,每次操作:1.连接2个点.2.断开2个点.3.查询2个点是否连通.m<=2*10^5. [算法]Link-Cut Tree [题解]LCT模板题,Link,Cut, ...

  3. 如何阻止自动更新‘updated_at’和'created_at'

    可以在模版中添加一条代码: public $timestamps = false;

  4. 使用inline-block,使前面img,后面空div居中显示在一行后,导致当div中有内容时,div下移问题

    .pro_li img,.pro_sm{display: inline-block; *display:inline;*zoom:1;vertical-align: middle ;} 解决方法:使用 ...

  5. python学习笔记(十四)之字典

    字典:是python中唯一的映射类型,字典中每一项都是由键-值对组成的项.字典中没有索引,只有键和值.键的类型可以是整型,变量或字符串. 创建和访问字典: >>> dict1 = { ...

  6. HDU 2082 找单词 (普通母函数)

    题目链接 Problem Description 假设有x1个字母A, x2个字母B,..... x26个字母Z,同时假设字母A的价值为1,字母B的价值为2,..... 字母Z的价值为26.那么,对于 ...

  7. 天梯赛 L2-006 树的遍历 (二叉树)

    给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出一个正整数N(<=30),是二叉树中结点的个数.第二行给出其后序遍历序 ...

  8. (3)剑指Offer之数值的整数次方和调整数组元素顺序

    一 数值的整数次方 题目描述: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 问题解析: 这道题算是比较麻烦和难一点的一个了.我这里采 ...

  9. 47、求1+2+3+...+n

    一.题目 求1+2+3+...+n,要求不能使用乘除法.for.while.if.else.switch.case等关键字及条件判断语句(A?B:C). 二.解法 public class Solut ...

  10. sqlalchemy更新和插入操作

    def save_app_info(self): try: # update app_info print(self.dicts) data = db_session.query(App_Info). ...