题面:[NOIP2012]疫情控制

题解:

  大体思路很好想,但是有个细节很难想QAQ

  首先要求最大时间最小,这种一般都是二分,于是我们二分一个时间,得到一个log。

  然后发现一个军队,越往上走肯定可以控制的叶节点越多,因此我们在时间范围内尽量向上走,又得到一个log了。

  如果一个军队走到根后还有多余时间,那它就有可能走到根的其他儿子去帮助其他子树。

  然后为了尽可能覆盖多的子树,显然应该要用剩余时间少的军队,对应走过去代价小的子树,所以sort一下就可以了?

  然而还有一种情况,那就是一个点从它的子树出发到了root,万一最后需要回到它自己那个子树,直接做就把代价算了2次,这样就可能导致本来可以不花代价就回到原来的子树的,但我们却花了双倍代价。。。。于是可能就把一个合法的时间判成不合法了。

  所以应该怎么做?

  其实只需要在 每个子树中可以走出去帮助其他子树的军队 里面选一个剩余时间最少的,走出去不能回来的军队守护自己就可以了(前提是自己还需要守护)。

  可以证明,这样肯定是最优的。

  因为如果把所有军队都提上去的话,意味这你要用军队x来帮助其他子树,同时意味着要从其他子树选一个军队y来帮助它。那么观察到既然军队x出来后无法回去,却可以帮助某个子树u,因此到这个被帮助的子树u的代价要比回去的代价小。所以如果我们用军队x来守护自己所在的子树,那么原本从其他子树中选出来帮助它的军队y就可以去守护子树u,因为子树u代价比当前子树小,因此子树u一定可以被军队y守护到。

  所以肯定不会变劣。

  写的时候还有一些细节,,,

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define R register int
  4. #define AC 51000
  5. #define ac 101000
  6. #define LL long long
  7.  
  8. int n, m, num, cnt, rnt;
  9. LL all;
  10. int Head[AC], Next[ac], date[ac], tot;
  11. int p[AC], father[AC][];
  12. LL st[AC][], have[AC], len[ac];
  13. bool z[AC], used[AC], vis[AC];
  14.  
  15. struct node{
  16. int x;
  17. LL rest;
  18. friend bool operator < (node a, node b){
  19. return a.rest < b.rest;
  20. }
  21. }s[AC], son[AC];
  22.  
  23. inline int read()
  24. {
  25. int x = ;char c = getchar();
  26. while(c > '' || c < '') c = getchar();
  27. while(c >= '' && c <= '') x = x * + c - '', c = getchar();
  28. return x;
  29. }
  30.  
  31. inline void add(int f, int w, int S)
  32. {
  33. date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S;
  34. date[++ tot] = f, Next[tot] = Head[w], Head[w] = tot, len[tot] = S;
  35. }
  36.  
  37. void pre()
  38. {
  39. n = read();
  40. for(R i = ; i < n; i ++)
  41. {
  42. int a = read(), b = read(), c = read();
  43. add(a, b, c), all += c;
  44. }
  45. father[][] = ;//父亲设为自己,防止子树里面的点跳到0
  46. m = read();
  47. for(R i = ; i <= m; i ++) p[i] = read();
  48. }
  49.  
  50. void dfs1(int x)//预处理倍增数组 get: st father have son
  51. {
  52. vis[x] = true;
  53. for(R i = ; i <= ; i ++)
  54. {
  55. father[x][i] = father[father[x][i - ]][i - ];
  56. st[x][i] = st[x][i - ] + st[father[x][i - ]][i - ];
  57. }
  58. int now;
  59. for(R i = Head[x]; i; i = Next[i])
  60. {
  61. now = date[i];
  62. if(vis[now]) continue;
  63. if(x == ) son[++ num] = (node){now, len[i]};
  64. father[now][] = x, st[now][] = len[i];
  65. have[now] = have[x] + len[i], dfs1(now);//记录下从root到now的距离
  66. }
  67. }
  68.  
  69. void dfs2(int x)//找到哪些节点还没有被控制
  70. {
  71. if(z[x]) return ;
  72. int now;bool done = true, flag = false;
  73. for(R i = Head[x]; i; i = Next[i])
  74. {
  75. now = date[i];
  76. if(now == father[x][]) continue;
  77. dfs2(now), flag = true;
  78. if(!z[now]) done = false;//如果儿子里面有一个不合法的,这个节点就不合法
  79. }//不能直接return,因为1号节点一般都不合法,但其他儿子还要标记的,,,
  80. if(flag) z[x] = done;//否则所有儿子都合法,那这个点就合法,但是如果这个点是叶子,,,就不能平白无故打标记了
  81. }
  82.  
  83. bool check(int mid)//判断这个时间是否合法
  84. {
  85. cnt = rnt = ;
  86. memset(z, , sizeof(z));
  87. for(R i = ; i <= m; i ++)
  88. {
  89. int x = p[i], now = ;
  90. for(R j = ; j >= ; j --)
  91. if(father[x][j] != && now + st[x][j] <= mid)
  92. now += st[x][j], x = father[x][j];//记得要先加后跳
  93. if(have[x] >= mid - now) z[x] = true;//无法到达别的子树
  94. else s[++ cnt] = (node){x, mid - now};//可以到达
  95. }
  96. dfs2();
  97. //sort(s + 1, s + cnt + 1);
  98. for(R i = ; i <= cnt; i ++)//分配一个不能回来的给当前子树
  99. if(s[i].rest < * have[s[i].x] && !z[s[i].x]) z[s[i].x] = true;
  100. else s[++ rnt] = s[i], s[rnt].rest -= have[s[i].x];//提到root的同时要加上去root的代价
  101. sort(s + , s + rnt + );//排序。
  102. int l = ;
  103. for(R i = ; i <= num; i ++)//剩下的从小到大依次匹配
  104. {
  105. if(z[son[i].x]) continue;
  106. while(s[l].rest < son[i].rest && l <= rnt) ++ l;
  107. if(l > rnt) return false;
  108. ++ l;//把这个用了
  109. }
  110. return true;
  111. }
  112.  
  113. void half()//二分时间
  114. {
  115. if(num > m) {printf("-1\n"); return ;}//如果root儿子数大于军队数,那么永远不可能全部覆盖
  116. sort(son + , son + num + );
  117. int l = , r = all, mid;
  118. while(l < r)
  119. {
  120. mid = (l + r) >> ;
  121. if(check(mid)) r = mid;
  122. else l = mid + ;
  123. }
  124. printf("%d\n", l);
  125. }
  126.  
  127. int main()
  128. {
  129. freopen("in.in", "r", stdin);
  130. pre();
  131. dfs1();
  132. half();
  133. fclose(stdin);
  134. return ;
  135. }

  

[NOIP2012]疫情控制 贪心 二分的更多相关文章

  1. Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增)

    Luogu 1084 NOIP2012 疫情控制 (二分,贪心,倍增) Description H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树, 1 号城市是首都, 也是 ...

  2. NOIP2012疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

  3. [NOIP2012]疫情控制(二分答案+倍增+贪心)

    Description H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 ...

  4. NOIP2012 疫情控制 题解(LuoguP1084)

    NOIP2012 疫情控制 题解(LuoguP1084) 不难发现,如果一个点向上移动一定能控制更多的点,所以可以二分时间,判断是否可行. 但根节点不能不能控制,存在以当前时间可以走到根节点的点,可使 ...

  5. 【NOIP2012】疫情控制(二分,倍增,贪心)

    洛谷上的题目链接,题目不在赘述 题解 既然要时间最短,首先考虑二分. 因此,考虑二分时间,问题转换为如何检查能否到达. 如果一支军队一直向上走,能够到达根节点,那么他可以通过根节点到达其他的节点,因此 ...

  6. NOIP2012疫情控制(二分答案+树上贪心)

    H 国有n个城市,这 n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点. H国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示 ...

  7. P1084 [NOIP2012 提高组] 疫情控制 (二分答案、贪心)

    因为若一个时间限制满足题意,则所有比它大的时间限制一定都满足题意,因此本题答案具有单调性,可以想到二分答案求解. 本题思路不是很难,但细节和代码实现比较复杂. 见牛人博客:https://www.lu ...

  8. NOIP 2012 疫情控制(二分+贪心+倍增)

    题解 二分时间 然后一个显然的事是一个军队向上爬的越高它控制的点越多 所以首先军队尽量往上爬. 当一个军队可以爬到根节点我们记录下它的剩余时间T和它到达根结点时经过的根节点的子节点son. 当一个军队 ...

  9. luogu1084 [NOIp2012]疫情控制 (二分答案+倍增+dfs序)

    先二分出一个时间,把每个军队倍增往上跳到不能再跳 然后如果它能到1号点,就记下来它跳到1号点后剩余的时间:如果不能,就让它就地扎根,记一记它覆盖了哪些叶节点(我在这里用了dfs序+差分,其实直接dfs ...

随机推荐

  1. apache Subversion 直接支持LDAP域群组

    如果你的Subversion已经用apache的ldap支持用户认证功能,你是否常常在想,既然都用ldap支持认证,为什么不直接支持域群组, 反而在authz文件里面一个一个的手工定义,或者有人用脚本 ...

  2. MySQL☞in语句

    in语句: 1)列名 in(数值1,数值2,数值3…):求出满足该列的多个列值 格式: select 列名1,列名2  from 表名 where 列名 in (数值1,数值2,数值3...) 如下图 ...

  3. Java开发工程师(Web方向) - 03.数据库开发 - 第2章.数据库连接池

    第2章--数据库连接池 数据库连接池 一般而言,在实际开发中,往往不是直接使用JDBC访问后端数据库,而是使用数据库连接池的机制去管理数据库连接,来实现对后端数据库的访问. 建立Java应用程序到后端 ...

  4. css多行文本溢出显示省略号(…)

    text-overflow:ellipsis属性可以实现单行文本的溢出显示省略号(…).但部分浏览器还需要加宽度width属性. css代码: overflow: hidden; text-overf ...

  5. Java学习笔记-10.io流

    1.输入流,只能从中读取数据,而不能向其写出数据.输出流,只能想起写入字节数据,而不能从中读取. 2.InputStream的类型有: ByteArrayInputStream 包含一个内存缓冲区,字 ...

  6. 转:Linux 编译安装 Mysql5.7

    http://broqiang.com/2017/04/18/Mysql-Install-5.7.18-Linux-Compile/ 原文 Linux 编译安装 Mysql5.7 Ubuntu 下快速 ...

  7. 美美哒rand()函数

     2016.3.7     天气:大雪都已经三月份竟然还下了这么大的雪,真是少见呀.今天为了提交软件工程的作业我们需要注册git的账号,真是前途艰难呀,后台服务器都要爆炸了,其实我觉得这个平台的服务器 ...

  8. Linux GCC编译

    .a 静态库(打包文件 由多个.o文件打包而成) .c 未经过预处理的C源码 .h C头文件 .i 经过预处理的C源码(将头文件内容加载到c文件中) .o 编译之后产生的目标文件 .s 生成的汇编语言 ...

  9. Java常用类之StringBuffer

    StringBuffer 类: 1. java.lang.StringBuffer 代表可变的字符序列: 2. StringBuffer 和 String 类似,但是 StringBuffer 可以对 ...

  10. 【redis数据库学习】用JAVA连接redis数据库各种报错

    最近项目中,需要用到redis数据库,然后使用Jedis让JAVA连接redis. 首先,安装redis数据库,参考的是:http://www.runoob.com/redis/redis-insta ...