题目大意

  给出一棵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. Xcode变量概览-summary

    问题描述 在Xcode中断点调试时,鼠标停留在变量上,就能看到变量的信息.但对于自定义对象,通常Xcode提供的直接信息非常有限,像这样 想要了解这个对象具体的内容,需要展开左边的箭头 当开发者想要知 ...

  2. How to reset XiaoMi bluetooth headphone Youth edition.

    To reset the speaker 1. Long press the phone call button to shut off the speaker 2. Connect the char ...

  3. Hibernate总结之常用API

    1. Configuration Configuration是用来读取配置文件,从配置文件中构件SessionFactory的. SessionFactory sessionFactory=new C ...

  4. WAMP允许外部访问的修改方法

    apache配置文件httpd.conf里的 "Require local"改" Require all granted"

  5. [转载]Windows服务编写原理及探讨(2)

    (二)对服务的深入讨论之上 上一章其实只是概括性的介绍,下面开始才是真正的细节所在.在进入点函数里面要完成ServiceMain的初始化,准确点说是初始化一个 SERVICE_TABLE_ENTRY结 ...

  6. 问候Maven3(笔记一)

    第一节:Maven 简介 百度百科:Maven 官网:http://maven.apache.org/ 第二节:Maven 安装与配置 Maven 下载:http://maven.apache.org ...

  7. 9. Swarm mode

  8. csu 1756(数论+去重)

    Prime Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 84  Solved: 12[Submit][Status][Web Board] Descr ...

  9. [Linux][Ubuntu18.04.1] nginx+php+MySQL环境搭建

    说在前面 今天在腾讯云的CVM服务器搭建了一下环境[主机:标准型S2,Unbuntu18.04的LST版本] 采用了nginx服务器(Nginx 静态处理性能比 Apache高3倍以上,不过apach ...

  10. jmeter-----查看结果树

    在编写接口测试脚本的时候,需要进行调试和查看结果是否正常的情况,这个时候可以使用查看结果树组件进行. 查看结果树中展示了每一个取样器的结果.请求信息和响应信息,可以查看这些内容去分析脚本是否存在问题. ...