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

题目:

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

也是树中的根节点。

H 国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境

城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境

城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,

首都是不能建立检查点的。

现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在

一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等

于道路的长度(单位:小时)。

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

分析:

首先,我们发现,对于给定的时间,每一个军队能不能到就是一定的了,那么我们自然可以想到二分答案,对于一个时间,能不能控制疫情呢?因为是一棵树,所以每一个军队越往上走越有利于控制整个国家,对于能到达首都的军队我们就让他到达首都,并且按照需要把它分配给首都底下的直辖市,进而控制另一个没有军队或是军队到不了的子树。具体实现看代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 50010
using namespace std;
struct node
{
    int to;
    int next;
    int w;
}edge[maxn*2];
struct point
{
    int gra;
    int v;
}extra[maxn],defic[maxn];
int head[maxn*2],arm[maxn],fath[maxn],gran[maxn],dis[maxn],cal[maxn],tim[maxn],b[maxn];
int n,m,tot=0,cnt=0,sum=0,na=0,nc=0;

bool comp(const point &a,const point &b)
{
    return a.v>b.v;
}
void add(int,int,int);//建图
void dfs(int);//处理出要被覆盖的点和已被覆盖的点
void cal_tim(int);//处理出每个军队的能力,能到,不能到
void cal_extra(int);//能到的还有多少剩余
void cal_defic();//不能到的还差多少
bool remedy(); //能到的能不能补齐不能到的
bool ok(int);//二分的时间是否够(其实就是调用以上四个函数)
void conn_anc(int);//处理出各个城市所属的直辖市,根节点的儿子
int erfen();//二分时间 

int main()
{
    cin>>n;
    memset(head,0,sizeof(head));
    for(int i=1;i<n;i++)
    {
        int x,y,ww;
        cin>>x>>y>>ww;
        add(x,y,ww);
        add(y,x,ww);
        if(x==1 || y==1)
            tot++;
    }
    cin>>m;
    if(tot>m)
    {
        cout<<-1;
        return 0;
    }
    conn_anc(1);
    for(int i=1;i<=m;i++)
        cin>>arm[i];
    sort(arm+1,arm+1+m);
    cout<<erfen();
    return 0;
}

void add(int u,int v,int ww)
{
    cnt++;
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    edge[cnt].w=ww;
    head[u]=cnt;
}

void conn_anc(int x)
{
    for(int i=head[x];i;i=edge[i].next)
    {
        int vv=edge[i].to;
        if(fath[x]!=vv)
        {
            if(x==1)
                gran[vv]=vv;
            else
                gran[vv]=gran[x];
            fath[vv]=x;
            dis[vv]=dis[x]+edge[i].w;
            conn_anc(vv);
        }
    }
}

void dfs(int root)
{
    int maxx=-1,now1=0,now2=0;
    for(int i=head[root];i;i=edge[i].next)
    {
        int vv=edge[i].to;
        if(fath[root]!=vv)
        {
            dfs(vv);
            if(tim[vv]==-1) now2=1;//该点尚未被覆盖
            if(tim[vv]>=edge[i].w)  now1=1;//该点可以被覆盖
            maxx=max(maxx,tim[vv]-edge[i].w);
        }
    }
    if(root!=1&&edge[head[root]].next)
    {
        if(now1) tim[root]=max(tim[root],maxx);
        else if(now2) tim[root]=max(tim[root],-1);
        else tim[root]=max(tim[root],0);
    }
}

void cal_tim(int ti)
{
    for(int i=1;i<=m;i++)
        if(dis[arm[i]]>=ti) tim[arm[i]]=ti;
    dfs(1);
}

void cal_extra(int ti)
{
    for(int i=1;i<=m;i++)
    {
        if(dis[arm[i]]<ti)
        {
            extra[++na].gra=gran[arm[i]];
            extra[na].v=ti-dis[arm[i]];
        }
    }
    sort(extra+1,extra+na+1,comp);
    for(int i=1;i<=na;i++)
    {
        if(!cal[extra[i].gra]) cal[extra[i].gra]=i;
        else if(extra[cal[extra[i].gra]].v>extra[i].v) cal[extra[i].gra]=i;
    }
}

void cal_defic()
{
    for(int i=head[1];i;i=edge[i].next)
    {
        int vv=edge[i].to;
        if(tim[vv]==-1)
        {
            defic[++nc].gra=vv;
            defic[nc].v=edge[i].w;
        }
    }
    sort(defic+1,defic+nc+1,comp);
}

bool remedy()
{
    if(na<nc) return false;
    memset(b,0,sizeof(b));
    int i=1,j=1;
    for(;i<=nc;i++)
    {
        if(!b[cal[defic[i].gra]] && cal[defic[i].gra])
            b[cal[defic[i].gra]]=1;
        else
        {
            while(j<=na && b[j]) j++;
            if(j>na || extra[j].v<defic[i].v)  return false;
            b[j]=1,j++;
        }
    }
    return true;
}

bool ok(int ti)
{
    memset(cal,0,sizeof(cal));
    na=nc=0;
    memset(tim,-1,sizeof(tim));
    cal_tim(ti);
    cal_extra(ti);
    cal_defic();
    return remedy();
}

int erfen()
{
    int l=-1,r=999999999;
    while(l+1<r)
    {
        int mid=(l+r)/2;
        if(ok(mid))  r=mid;
        else l=mid;
    }
    return r;
}

时间: 2024-08-25 21:14:33

洛谷 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 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以在有道路连接的城

NOIP2012 洛谷P1084 疫情控制

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

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

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

luogu P1084疫情控制 二分

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

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

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

[luogu]P1084 疫情控制

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

洛谷P1083 借教室 二分 + 差分

洛谷P1083 借教室 二分 + 差分(或说前缀和,其实前缀和更准确一点) 首先二分答案,即取 mid 个人,且他们不会冲突 然后O(n) 判断是否冲突 如何判断呢,首先我们发现 一个人的操作相当于是将 一些连续的山削去了一个高度 然后我们可以记录这座山被消了多少高度,但这样一次就要 O(N) 总共(n^2) 但是我们发现高度差只有两个地方变了,一个是起始,一个是终止 t[ i ] 表示 h[ i ] - h[ i-1 ] 改变过后 于是 t[ s ]-=d,t[ t+1 ]+=d ; 然后这样

洛谷P1316 丢瓶盖 二分答案

洛谷P1316 丢瓶盖 二分答案 二分距离 判断能够取几个 1 #include <bits/stdc++.h> 2 #define For(i,j,k) for(int i=j;i<=k;i++) 3 using namespace std ; 4 5 const int N = 100011 ; 6 int n,k,mi,mx,mid,l,r ; 7 int last,sum ; 8 int pos[N] ; 9 10 inline int read() 11 { 12 int x