【NOIP】提高组2012 疫情控制

【题意】n个点的树,1为根,要求删除一些点使得截断根节点和所有叶子结点的路径(不能删根,可以删叶子)。有m支军队在m个点上,每时刻所有军队可以走一步,最终走到的地方就是删除的点,求最短时间。

【题解】

所有点同时走路,求最短时间,这样的询问通常考虑二分转化为判定性问题。(实际上,这题用二分确实没有想到,如果能想到二分整道题就好写一些了)

容易发现,每支军队贪心地往上走最优。

那么对于二分的时间,有一部分军队可以到达根,A数组记录这些军队到达根后的剩余时间,待会可以走到第二层覆盖其它节点。

有一部分军队不能到达根,处理出这些军队能覆盖多少二层节点,B数组不能覆盖的二层节点到根的路径。

AB各自排序之后,对应匹配,若A能将B全部匹配就可以满足要求,否则不能。

还有一个问题,一个军队虽然不能到达根后返回来覆盖自己,但可以直接不去根。解决方法是从小到大枚举A时,如果该点本身的二层节点还没覆盖就直接覆盖(因为该点本来就是最劣的,只要能覆盖一个二层结点就不亏)。

最后的问题是处理出不能到达根的军队能覆盖多少二层结点?可以对每个军队倍增,也可以直接一遍dfs。

dfs的具体做法是:c[x]表示x被覆盖,t[x]表示x子树的军队到x的最大剩余时间。c[x] = c[son[x]]=1 || t[x]>=0。son[x]表示x的所有儿子。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define ll long long
using namespace std;
int read(){
    char c;int s=0,t=1;
    while(!isdigit(c=getchar()))if(c==‘-‘)t=-1;
    do{s=s*10+c-‘0‘;}while(isdigit(c=getchar()));
    return s*t;
}
const int maxn=100010;
int n,m,tot,first[maxn],top[maxn],a[maxn];
ll dis[maxn],t[maxn];
bool b[maxn],c[maxn];
struct edge{int v,w,from;}e[maxn*2];
void insert(int u,int v,int w){tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
void DFS(int x,int fa,int tp){
    top[x]=tp;
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        dis[e[i].v]=dis[x]+e[i].w;//yu ju shun xu
        DFS(e[i].v,x,tp);
    }
}
void dfs(int x,int fa){
    c[x]=0;
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){c[x]=1;break;}
    for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
        dfs(e[i].v,x);
        c[x]&=c[e[i].v];
        t[x]=max(t[x],t[e[i].v]-e[i].w);
    }
    if(t[x]>=0)c[x]=1;
}
int totA,totB;
struct cyc{ll num;int id;}A[maxn],B[maxn];//
bool cmp(cyc a,cyc b){return a.num<b.num;}
bool check(ll time){
    memset(t,-1,sizeof(t));
    totA=0;totB=0;
    for(int i=1;i<=m;i++)if(dis[a[i]]<=time)b[i]=1,A[++totA].num=time-dis[a[i]],A[totA].id=top[a[i]];
        else b[i]=0,t[a[i]]=time;
    dfs(1,0);
    for(int i=first[1];i;i=e[i].from)if(!c[e[i].v])B[++totB].num=e[i].w,B[totB].id=e[i].v;
    sort(A+1,A+totA+1,cmp);sort(B+1,B+totB+1,cmp);
    int now=1;
    for(int i=1;i<=totA;i++){
        while(now<=totB&&c[B[now].id])now++;
        if(!c[A[i].id]){c[A[i].id]=1;continue;}
        if(now<=totB&&A[i].num>=B[now].num){c[B[now++].id]=1;}
    }
    while(now<=totB&&c[B[now].id])now++;//
    if(now>=totB+1)return 1;
    return 0;
}
int main(){
    n=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        insert(u,v,w);insert(v,u,w);
    }
    memset(dis,0,sizeof(dis));
    for(int i=first[1];i;i=e[i].from)dis[e[i].v]=e[i].w,DFS(e[i].v,1,e[i].v);
    m=read();
    for(int i=1;i<=m;i++)a[i]=read();
    ll l=0,r=1ll*n*1e9+1,mid;
    while(l<r){
        mid=(l+r)>>1;
        if(check(mid))r=mid;else l=mid+1;
    }
    if(r>1ll*n*1e9)printf("-1");else printf("%lld",l);
    return 0;
}

时间: 2024-10-30 06:35:53

【NOIP】提高组2012 疫情控制的相关文章

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

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

【noip 2012】提高组Day2T3.疫情控制

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

noip提高组2012 借教室(luogu 1083)

原题链接:https://www.luogu.org/problem/show?pid=1083 坐在我对面的dalao瞬间就写出了线段树的解法,蒟蒻线段树太弱,只能写前缀和. 大致分析了一下,二分答案每天能否能满足当天的需求,能就向后找,不能就向前找, 找到最后都能满足,那就是能满足了,不然,此时就会停留在第一个不能满足的时刻. (终于会用插入代码的操作了(我太弱了...)) #include<cstdio> #include<cstring> using namespace s

Vijos P1002 过河 (NOIP提高组2005)

链接:https://www.vijos.org/p/1002 解析: 若 p*x+(p+1)*y=Q(采用跳跃距离p和p+1时可以跳至任何位置Q),则在Q ≥ P*(P-1)时是一定有解的. 由于题目给出的一个区间是1≤S≤T≤10,于是当相邻的两个石子之间的距离不小于8*9=72时,则后面的距离都可以到达,我们就可以认为它们之间的距离就是72.如此一来,我们就将原题L的范围缩小为了100*72=7200,动态规划算法完全可以承受了. 但是当S=T时,上述等式是无法使用的,在这种情况下,只需要

NOIP提高组2004 合并果子题解

NOIP提高组2004 合并果子题解 描述:在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和.可以看出,所有的果子经过n-1次合并之后,就只剩下一堆了.多多在合并果子时总共消耗的体力等于每次合并所耗体力之和. 因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力.假定每个果子重量都为1,并且已知果子的种类数和每种果子的数目,你的任务是设计出

NOIP 提高组2013 火柴排队 (Vijos P1842)

描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:∑ i=1 n (a i ?b i ) 2  ,其中 a i   表示第一列火柴中第 i 个火柴的高度,b i   表示第二列火柴中第 i 个火柴的高度. 每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小.请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模

NOIP提高组 1999 &amp; 2000 题解合集

[序言]话说我在学神奇算法的时候,基础应该也要巩固,于是打算提前把NOIP提高组的刷完. 具体的题目描述和提交我就在VIJOS上完成. [1999.1] 描述 给定一个信封,最多只允许粘贴N张邮票,计算在给定M(N+M<=10)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大max ,使得1-max之间的每一个邮资值都能得到. 例如,N=3,M=2,如果面值分别为1分.4分,则在l分-6分之间的每一个邮资值都能得到(当然还有8分.9分和12分):如果面值分别为1分.3分

2017.11.25【NOIP提高组】模拟赛A组

2017.11.25[NOIP提高组]模拟赛A组 T1 3467. [NOIP2013模拟联考7]最长上升子序列(lis) T2 3468. [NOIP2013模拟联考7]OSU!(osu) T3 3472. [NOIP2013模拟联考8]匹配(match) T1 有转移方程f[i]=max{f[j]}+1,a[j]<a[i] 可以用线段树+离散化维护这个方程,因为涉及以往状态可以用主席树维护 打太丑爆空间了 Code 1 #include<cstdio> 2 #include<c

2017.12.02【NOIP提高组】模拟赛A组

2017.12.02[NOIP提高组]模拟赛A组 T1 3555[GDKOI2014模拟]树的直径 T2 3542[清华集训2014]冒泡排序 T3 3486[NOIP2013模拟联考10]道路改建(rebuild) T1 树直径的一个性质,两棵树合并,形成新的树的直径的两个端点为原树中的四个端点之二. 可以用反证法证明.用此性质本题就变成了lca裸题了 Code #include<cstdio> #include<cstring> #include<cmath> #i