NOIP2012 洛谷P1084 疫情控制

Description:

H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。

请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

Input:

第一行一个整数 n,表示城市个数。

接下来的 n-1 行,每行 3 个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市 u 到城市 v 有一条长为 w 的道路。数据保证输入的是一棵树,且根节点编号为 1。

接下来一行一个整数 m,表示军队个数。

接下来一行 m 个整数,每两个整数之间用一个空格隔开,分别表示这 m 个军队所驻扎的城市的编号。

Output:

共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

思路:首先二分答案,然后对于每支军队,到根节点的子节点的距离小于mid的全都到根节点的子节点集中,其他军队都尽量往上走,能走到哪就是哪。然后将根节点的子节点上的军队剩余时间排序,剩余时间最小且无法做到去一趟根节点再回来的军队驻守在原来的子节点上,其他军队都到根节点上,然后排序,并将所有没有军队的子节点排序,贪心扫描一遍即可

#include<algorithm>
#include<iostream>
#include<vector>
#include<cstring>
#include<cstdio>
#define ll long long
using namespace std;
const int N = 50050;
typedef pair<int,int> P;

int head[N], now;
struct edges{
    int to, next, w;
}edge[N<<1];
void add(int u,int v,int w){ edge[++now] = {v, head[u], w}; head[u] = now;}
//dep存储深度,pre存储每个点父节点,e_pre存储每个点的上一条边 dict存储每个点所对应的根节点的子节点
//pos存储军队初始位置 f存储每个根节点的子节点上的病毒能否到达叶子
int n, m, pre[N], pos[N], dict[N], e_pre[N];
ll l, r, tot, dep[N];
bool stop[N], f[N];  //stop指那些无法到达根节点的子节点的军队停留的位置
vector<int> id[N];  //id存储每个根节点的子节点下的军队
vector<P> vec[N];  //该表存储的是每个根节点的子节点上驻扎的军队以及剩余时间
vector<int> A, B;  //该表存储的是军队的剩余时间和每个军队到根节点后再到各个子节点的距离 

int dfs1(int x,int root){  //一趟dfs获取深度,父节点等信息
    if(id[x].size()){
        for(int i = 0; i < id[x].size(); i++)
          dict[id[x][i]] = root;
    }
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(v == pre[x]) continue;
        pre[v] = x;
        e_pre[v] = i;
        dep[v] = dep[x] + edge[i].w;
        dfs1(v, root);
    }
}

bool dfs2(int x){
    bool flag = 0;
    if(stop[x]) return 1;
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(v == pre[x]) continue;
        flag = 1;
        if(!dfs2(v)) return 0;
    }
    if(!flag) return 0;
    return 1;
}

void init(){
    A.clear();
    B.clear();
    for(int i = 1; i <= n; i++)
      vec[i].clear();
    memset(stop,0,sizeof(stop));
    memset(f,0,sizeof(f));
}
void work(int x,int lim){
    while(edge[e_pre[x]].w <= lim){
        lim -= edge[e_pre[x]].w;
        x = pre[x];
    }
    stop[x] = 1;
}
bool cmp(P x, P y){ return x.second < y.second; }
bool che(int lim){
    init();
    for(int i = 1; i <= m; i++){
        int p = pos[i];
        if(dep[p] - dep[dict[i]] > lim){
            work(p, lim); continue;  //暴力向上走 把军队驻扎到离根节点最近的位置
        }
        vec[dict[i]].push_back(P(i, lim - (dep[p] - dep[dict[i]])));
    }
    for(int i = head[1]; i; i = edge[i].next){  //判断根节点的每个子节点是否有病毒传播到叶子节点
        f[edge[i].to] = dfs2(edge[i].to);
    }
    for(int i = head[1]; i; i = edge[i].next){
        int v = edge[i].to;
        bool flag = 0;
        if(vec[v].empty()) if(!f[v]){  //如果该节点上没有军队只能靠其他军队救济
            B.push_back(edge[i].w);
            continue;
        }
        sort(vec[v].begin(), vec[v].end(), cmp);//将每个点排序之后贪心地将剩余时间最短的军队驻扎在原地
        int tmp = vec[v][0].second;  //取出剩余时间最少的点
        if(tmp - 2 * edge[i].w < 0 && !f[v])  flag = 1; //该节点已经被管制
        else A.push_back(tmp - edge[i].w);
        for(int j = 1; j < vec[v].size(); j++){
            int x = vec[v][j].first, t = vec[v][j].second;//将子节点上的军队全都调及到根节点
            A.push_back(t - edge[i].w);
        }
        if(!flag && !f[v])
          B.push_back(edge[i].w);
    }
    sort(A.begin(), A.end()); // 贪心扫描 剩余时间最少的军队去最近的子节点
    sort(B.begin(), B.end());
    int s = 0;
    for(int i = 0; i < A.size(); i++){
        if(s == B.size()) break;
        if(A[i] >= B[s]) s++;
    }
    if(s == B.size()) return 1;
    else return 0;
}
int main(){
//    freopen("testdata.in","r",stdin);
    scanf("%d",&n);
    int x, y, z;
    for(int i = 1; i < n; i++){
        scanf("%d%d%d",&x, &y, &z);
        add(x, y, z); add(y, x, z);
        tot += z;
    }
    scanf("%d",&m);
    for(int i = 1; i <= m; i++)
      scanf("%d",&pos[i]), id[pos[i]].push_back(i);
    for(int i = head[1]; i; i = edge[i].next){
        dep[edge[i].to] = edge[i].w;
        pre[edge[i].to] = 1, e_pre[edge[i].to] = i;
        dfs1(edge[i].to, edge[i].to);
    }
    r = tot;
    ll ans = 1e15;
    while(l <= r){
        ll mid = l + r >> 1;
        if(che(mid)) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    if(ans == 1e15) puts("-1");
    else printf("%lld\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Rorshach/p/8684797.html

时间: 2024-07-30 02:15:52

NOIP2012 洛谷P1084 疫情控制的相关文章

洛谷P1084 疫情控制

题目:https://www.luogu.org/problemnew/show/P1084 这道题由于洛谷数据有点水,如果有vijos的推荐去vijos交一下,他的数据还好. vijos:https://vijos.org/p/1783 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立

[NOIP2012] 提高组 洛谷P1084 疫情控制

题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境 城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是, 首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以在有道路连接的城

洛谷 P1084 疫情控制 【二分+数据结构】

题目: H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境 城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是, 首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以在有道路连接的城市

洛谷P1084 [NOIP2012提高组Day2T3]疫情控制

P1084 疫情控制 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从 首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是,首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以

[luogu]P1084 疫情控制

原题链接:P1084 疫情控制 题意 给定一棵带权树,$1$号点为根节点,某些点上有军队. 现在要求移动这些军队,使军队覆盖所有的叶子节点,求移动距离最大值的最小值. 分析 很难直接求出最小值,我们可以考虑二分这个最小值,让原问题转化为判定问题. 二分最小值,我们只需要判断能否在$mid$距离内使军队全部移动到覆盖所有的叶子点. 1.上移军队 一个军队往哪个方向移动贡献最大? 明显是往根节点方向移动. 所以很明显我们第一步就是要把所有的节点尽可能地往上移动. 如果移动到顶(处于根节点的儿子节点)

P1084 疫情控制

题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是,首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以在有道路连接的城市间移动

Luogu P1084 疫情控制 | 二分答案 贪心

题目链接 观察题目,答案明显具有单调性. 因为如果用$x$小时能够控制疫情,那么用$(x+1)$小时也一定能控制疫情. 由此想到二分答案,将问题转换为判断用$x$小时是否能控制疫情. 对于那些在$x$小时内不能够走到根节点的子节点上的军队,让他们尽量往上走即可,走到哪里是哪里,这样显然不会更劣. 对于那些在$x$小时内能走到根节点的子节点上的军队,就让他们先走到根节点的子节点上. 然后搞搞贪心即可. #include<iostream> #include<cstdio> #incl

AC日记——餐巾计划问题 洛谷 P1084

餐巾计划问题 思路: 氧气优化水过: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 4005 #define maxque 1000005 #define INF 0x7fffffff #define ll long long ll n,head[maxn],E[maxque],V[maxque],W[maxque],F[maxque]; ll pi,qt,qc,st,sc,s,t,cnt=1,day[maxn],

luogu P1084疫情控制 二分

链接 luogu 思路 二分. 每个军队在一定的时间内越往上越好. 注意一个军队可以跨过1去帮别的. 把能到1脚下的点都存下来特判. 有一种情况是这个子树内只有一个军队,但这个军队跑去帮别人了. 其他军队来帮这个子树. 就像这样. 四号点的军队还有2秒钟,而且四号点有两个军队. 2号点有一个军队,还有101秒钟. 三号点没有军队. 四号点的一个军队到2,二号点的的军队到三. 这样的2号点放弃内部去外部的条件是什么? 答案是两倍的边权<剩余时间. 不然的话,四号店可以直接去拯救三号点了. 代码 #