题目: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 }