洛谷P1084 疫情控制

题目:https://www.luogu.org/problemnew/show/P1084

这道题由于洛谷数据有点水,如果有vijos的推荐去vijos交一下,他的数据还好。

vijos:https://vijos.org/p/1783

题目描述

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

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

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

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

输入输出格式

输入格式:

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

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

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

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

输出格式:

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

输入输出样例

输入样例#1:

4
1 2 1
1 3 2
3 4 3
2
2 2

输出样例#1:

3

说明

【输入输出样例说明】

第一支军队在 2 号点设立检查点,第二支军队从 2 号点移动到 3 号点设立检查点,所需时间为 3 个小时。

【数据范围】

保证军队不会驻扎在首都。

对于 20%的数据,2≤ n≤ 10;

对于 40%的数据,2 ≤n≤50,0<w <10^5;

对于 60%的数据,2 ≤ n≤1000,0<w <10^6;

对于 80%的数据,2 ≤ n≤10,000;

对于 100%的数据,2≤m≤n≤50,000,0<w <10^9。

NOIP 2012 提高组 第二天 第三题

解析:

感谢xun提供的代码让我成功找到了自己的错。

写的非常好,传送门:https://www.cnblogs.com/zzyh/p/7527945.html

。。。。。。这道题码完用了1h,调错用了2daysorz。

刚看完题:把靠首都最近的军队扔到首都就行了啊。。。

???首都不让去。。。。。。

那就每条与首都相连的边放个军队就行了啊。。。

但这似乎不是最优的,如果军队都已经在叶节点站好了呢。。。。。。

好了正经想一想。

什么时候不能呢?

如果军队把与首都相连的所有节点都覆盖了,那么就有解。

由于时间没有限制,那么只要军队够,覆盖完只是时间问题。

所以只要军队数m>=与首都相连的节点数,他就有解,否则无解。

 1 for (ll i=1;i<=n-1;++i){
 2     scanf("%lld%lld%lld",&ra,&rb,&rc);
 3     addedge(ra,rb,rc);
 4     addedge(rb,ra,rc);
 5     if (ra==1||rb==1) ++tot;
 6 }
 7 scanf("%lld",&m);
 8 if (m<tot){
 9     printf("-1\n");
10     return 0;
11 }

处理无解情况

刚才的思路提示我们越往上答案越优。

答案具有单调性,而且范围这么大,二分吧。

二分出时间咋办呢。。。

如果到不了首都的话,就让他能走多远就走多远,因为越往上是越优的。

如果到了的话,那就让他现在首都先呆着。

到首都的军队要负责覆盖与首都相通的每一个节点。

怎么分配时最优的呢?

我们先预处理出来这条路需不需要军队。

橙点代表已经被军队覆盖。

那么,2号节点由于子节点3、4已经被覆盖,所以2号节点可以直接视为已经覆盖,而不用再去分配军队。

但6号节点就不行,因为有6号节点的儿子7节点没有覆盖,所以要从到达首都的军队中找一支军队覆盖6节点。

这个处理可以用一个dfs解决,

如果出错了,考虑一下叶节点的处理是否正确orz。

 1 void dfs_cover(ll x){
 2     bool noleaf=false,allcover=true;
 3     for (ll i=0;i<G[x].size();++i){
 4         line e=edge[G[x][i]];
 5         if (e.to==fa[x][0]) continue;
 6         noleaf=true;
 7         if (!covered[e.to]) allcover=false;
 8         dfs_cover(e.to);
 9     }
10     if (noleaf&&allcover&&x!=1) covered[x]=true;
11         //如果你像我这样用allcover表示全部覆盖时
12         //如果该节点是叶节点,那么allcover全是true,gg
13         //如果该节点是根节点,那么根节点被覆盖后也会gg
14         //处理时要注意细节orz
15 }

处理已经覆盖的节点

然后我们分配跑到首都的节点。

①如果他原先所在的那条路还没有覆盖,就撤回去把自己家覆盖了吧。。。

自己家都没看好去看别人家绝对是石乐志。。。

②如果他原先所在的那条路被覆盖了,那他就有余力去搞其他节点啦。

距首都最近的当然要用剩余距离短的去覆盖啦。贪心思路确定。

排个序,一个一个覆盖,如果能覆盖完,合法,否则需要扩大时间。

因此我们要维护出到达首都的军队从哪里来,剩余的距离。

与首都相连未覆盖的节点与首都的距离即可。

由于路径比较长,所以来个倍增吧。。。

 1 void dfs(ll x){
 2     for (ll i=0;i<G[x].size();++i){
 3         line e=edge[G[x][i]];
 4         if (e.to==fa[x][0]) continue;
 5         fa[e.to][0]=x;
 6         g[e.to][0]=e.v;
 7         if (x==1) root[e.to]=e.to;
 8         else root[e.to]=root[x];
 9         dfs(e.to);
10     }
11 }
12 //-----------------------------------------------------------------
13 dfs(1);
14 for (ll j=1;j<=29;++j){
15     for (ll i=1;i<=n;++i){
16         fa[i][j]=fa[fa[i][j-1]][j-1];
17         g[i][j]=g[fa[i][j-1]][j-1]+g[i][j-1];
18     }
19 }

倍增

好了,思路就是这样了。

代码量也就这样吧。

调错是痛苦的orz。

我犯的错:

①两个dfs,一个dfs_cover()中顺手打了个dfs()。。。。。。。

②处理边的时候,x和i又搞混了。。。。。。

希望以后注意注意吧。。。。。。

代码如下:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<vector>
  7 using namespace std;
  8 #define maxn 50010
  9 #define ll long long
 10 struct line{
 11     ll from,to;
 12     ll v;
 13 };
 14 struct reached{
 15     ll root;
 16     ll rest;
 17 }reach[maxn];
 18 ll reach_tot;
 19 bool cmpa(const reached &a,const reached &b){
 20     return a.rest<b.rest;
 21 }
 22 struct uncov{
 23     ll pos;
 24     ll v;
 25 }uncovered[maxn];
 26 ll uncovered_tot;
 27 bool cmpb(const uncov &a,const uncov &b){
 28     return a.v<b.v;
 29 }
 30 ll n;
 31 ll ra,rb;
 32 ll rc;
 33 vector<line> edge;
 34 vector<ll> G[maxn];
 35 ll m;
 36 ll army[maxn];
 37 ll tot;
 38 ll fa[maxn][30],g[maxn][30];
 39 ll root[maxn];
 40 ll l,r,ans;
 41 bool covered[maxn];
 42 void addedge(ll from,ll to,ll val){
 43     edge.push_back((line){from,to,val});
 44     int m=edge.size();
 45     G[from].push_back(m-1);
 46 }
 47 void dfs(ll x){
 48     for (ll i=0;i<G[x].size();++i){
 49         line e=edge[G[x][i]];
 50         if (e.to==fa[x][0]) continue;
 51         fa[e.to][0]=x;
 52         g[e.to][0]=e.v;
 53         if (x==1) root[e.to]=e.to;
 54         else root[e.to]=root[x];
 55         dfs(e.to);
 56     }
 57 }
 58 void dfs_cover(ll x){
 59     bool noleaf=false,allcover=true;
 60     for (ll i=0;i<G[x].size();++i){
 61         line e=edge[G[x][i]];
 62         if (e.to==fa[x][0]) continue;
 63         noleaf=true;
 64         if (!covered[e.to]) allcover=false;
 65         dfs_cover(e.to);
 66     }
 67     if (noleaf&&allcover&&x!=1) covered[x]=true;
 68 }
 69 bool check(ll x){
 70     memset(covered,false,sizeof(covered));
 71     memset(reach,0,sizeof(reach));
 72     reach_tot=0;
 73     memset(uncovered,0,sizeof(uncovered));
 74     uncovered_tot=0;
 75     for (ll i=1;i<=m;++i){
 76         ll u=army[i],mv=x;
 77         for (ll j=29;j>=0;--j){
 78             if (g[u][j]<=mv&&fa[u][j]){
 79                 mv-=g[u][j];
 80                 u=fa[u][j];
 81             }
 82         }
 83         if (u==1){
 84             reach[++reach_tot]=(reached){root[army[i]],mv};
 85         }else{
 86             covered[u]=true;
 87         }
 88     }
 89     dfs_cover(1);
 90     for (ll i=0;i<G[1].size();++i){
 91         line e=edge[G[1][i]];
 92         if (covered[e.to]) continue;
 93         uncovered[++uncovered_tot]=(uncov){e.to,e.v};
 94     }
 95     if (uncovered_tot>reach_tot) return false;
 96     sort(reach+1,reach+1+reach_tot,cmpa);
 97     sort(uncovered+1,uncovered+1+uncovered_tot,cmpb);
 98     ll solved=1;
 99     for (ll i=1;i<=reach_tot;++i){
100         if (!covered[reach[i].root]){
101             covered[reach[i].root]=true;
102         }else{
103             while (covered[uncovered[solved].pos]) ++solved;
104             if (solved>uncovered_tot) return true;
105             if (reach[i].rest>=uncovered[solved].v){
106                 covered[uncovered[solved++].pos]=true;
107             }
108         }
109         while (covered[uncovered[solved].pos]) ++solved;
110         if (solved>uncovered_tot) return true;
111     }
112     return solved>uncovered_tot;
113 }
114 int main(){
115     scanf("%lld",&n);
116     for (ll i=1;i<=n-1;++i){
117         scanf("%lld%lld%lld",&ra,&rb,&rc);
118         addedge(ra,rb,rc);
119         addedge(rb,ra,rc);
120         if (ra==1||rb==1) ++tot;
121     }
122     scanf("%lld",&m);
123     if (m<tot){
124         printf("-1\n");
125         return 0;
126     }
127     for (ll i=1;i<=m;++i){
128         scanf("%lld",&army[i]);
129     }
130     dfs(1);
131     for (ll j=1;j<=29;++j){
132         for (ll i=1;i<=n;++i){
133             fa[i][j]=fa[fa[i][j-1]][j-1];
134             g[i][j]=g[fa[i][j-1]][j-1]+g[i][j-1];
135         }
136     }
137     l=0; r=(ll)10000000000;
138     if (!check(r)){
139         printf("-1\n");
140         return 0;
141     }
142     while (l<=r){
143         ll mid=(l+r)/2;
144         if (check(mid)){
145             ans=mid;
146             r=mid-1;
147         }else{
148             l=mid+1;
149         }
150     }
151     printf("%lld",ans);
152     return 0;
153 }

时间: 2024-11-06 13:34:06

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

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

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

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

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

NOIP2012 洛谷P1084 疫情控制

Description: 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号点放弃内部去外部的条件是什么? 答案是两倍的边权<剩余时间. 不然的话,四号店可以直接去拯救三号点了. 代码 #