拿到题目的第一眼

首先这是一棵n个节点的树(别说你看不出来)

然后对于树上的m条链
我们可以选取树上的唯一一条边使它的边权变为0

求处理后最长链的长度

20分

m=1
好啦,好像可做
一眼望去全是水

只需求出一条链上的所有边并计算边权和及最大边权(暴力往上跳并记录即可)
边权和减去最大边权即为答案
那么我们就可以O(n)过掉这道题了(不嫌麻烦的话也可以O(log n)搞树上路径)

60分?

从未如此接近满分
全是链,这意味着什么(并不意味着什么)

想了想,发现好像60分并不好搞
考虑一下暴力吧

超级暴力:暴力枚举删每一条边,统计删完这条边之后最长链的长度,取最小值就是答案,复杂度O(n^2 m),25分

刚才的小优化

考虑优化暴力

枚举删哪条边O(n)显然已经达到理论下限
如果非要搞它的话只能排除那些不被经过的边,效率高不了多少

接下来是统计每条链的长度
全是链哎,求线性区间和,前缀和优化,消去一个O(n)

那个O(m)好像没有什么有效的优化

这样,复杂度降至O(nm),40分

然后其他数据,搞树链剖分动态修改、查询可以多拿一些分,复杂度O(nm log n),60分

怎么办

QAQ,60分都拿不到了吗

可不可以不实际改边权呢?

经过不会就猜二分
经过深入思考,我们发现:

最短时间为t,前提是对于length>t的所有链,总能找到至少一条长为k公共边,使得最长链的长度max length-k<=t
如果知道答案,好像不仅不用枚举最长链,还可以把枚举删边变为贪心删掉被全部满足条件的链经过的最长边,稳赚一个O(n)和一个O(m)

考虑二分答案

如果能够在时间t1内完成任务,那么对于t2>t1,总能在时间t2内完成任务
所以答案符合单调性
可以二分答案

Check函数怎么写呢,看一看能不能找到找到至少一条长为k公共边,使得最长链的长度max length-k<=t
设length>t的边的个数为number
我们必须知道一条边是否曾被number个链同时经过,唯一的方法好像就是差分了,check函数可以写成O(n + m)的,总复杂度O((n + m)log n),60分

100分

二分答案的做法放到树上呢

考虑线性数据上二分的完整做法
预处理每一条链的length,二分答案,放到check函数里搞

没问题
LCA求出每条链的length,还是二分,check函数换成树上差分
最后发现正解只要一句话:
求链长+二分

上代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<algorithm>
  4. #include<cstring>
  5. using namespace std;
  6. const int maxn=3e5+;
  7. struct edge{
  8. int next,to,dis;
  9. }e[*maxn],q[*maxn];
  10. struct length{
  11. int len,lca,u,v;
  12. }len[maxn];
  13. int head[maxn],cnt,headq[maxn],dis[maxn],maxlen,n,m,a[maxn],ans,s[maxn],num,ret,f[maxn]; //一堆变量
  14. bool vis[maxn];
  15. inline int readn() //随处可见的快读
  16. {
  17. int x=;
  18. char ch=getchar();
  19. while(ch>''||ch<'')
  20. ch=getchar();
  21. while(ch>=''&&ch<='')
  22. {
  23. x=(x<<)+(x<<)+(ch^'');
  24. ch=getchar();
  25. }
  26. return x;
  27. }
  28. inline void add_edge(int x,int y,int d)
  29. {
  30. e[++cnt].next=head[x];
  31. e[cnt].to=y;
  32. e[cnt].dis=d;
  33. head[x]=cnt;
  34. }
  35. inline void add_que(int x,int y)
  36. {
  37. q[++cnt].next=headq[x];
  38. q[cnt].to=y;
  39. headq[x]=cnt;
  40. }
  41. int find(int x)
  42. {
  43. return f[x]==x?f[x]:f[x]=find(f[x]); //一行并查集
  44. }
  45. void dfs(int u,int pre)
  46. {
  47. for(int i=head[u];i;i=e[i].next)
  48. {
  49. int v=e[i].to;
  50. if(v==pre)
  51. continue;
  52. dfs(v,u);
  53. s[u]+=s[v]; //统计经过次数
  54. }
  55. if(s[u]==num&&a[u]>ret)
  56. ret=a[u]; //贪心地选取最长公共边
  57. }
  58. inline bool check(int x)
  59. {
  60. memset(s,,sizeof(s));
  61. num=ret=;
  62. for(int i=;i<=m;i++) //树上差分
  63. if(len[i].len>x)
  64. {
  65. s[len[i].u]++;
  66. s[len[i].v]++;
  67. s[len[i].lca]-=;
  68. num++; //记录len>x的链的个数
  69. }
  70. dfs(,); //跑差分结果
  71. if(maxlen-ret>x) //如果不能满足,返回NO
  72. return ;
  73. return ; //能满足
  74. }
  75. void tarjan(int u,int pre) //tarjan求链长
  76. {
  77. for(int i=head[u];i;i=e[i].next)
  78. {
  79. int v=e[i].to;
  80. if(v==pre)
  81. continue;
  82. dis[v]=dis[u]+e[i].dis;
  83. tarjan(v,u);
  84. a[v]=e[i].dis;
  85. int f1=find(v);
  86. int f2=find(u);
  87. if(f1!=f2)
  88. f[f1]=find(f2);
  89. vis[v]=;
  90. }
  91. for(int i=headq[u];i;i=q[i].next)
  92. if(vis[q[i].to])
  93. {
  94. int p=(i+)>>;
  95. len[p].lca=find(q[i].to);
  96. len[p].len=dis[u]+dis[q[i].to]-*dis[len[p].lca];
  97. maxlen=max(maxlen,len[p].len);
  98. }
  99. }
  100. int main()
  101. {
  102. n=readn(),m=readn();
  103. for(int i=;i<n;i++)
  104. {
  105. int ai=readn(),bi=readn(),ti=readn();
  106. add_edge(ai,bi,ti); //邻接表存图
  107. add_edge(bi,ai,ti);
  108. }
  109. for(int i=;i<=n;i++)
  110. f[i]=i;
  111. cnt=;
  112. for(int i=;i<=m;i++)
  113. {
  114. int x=readn(),y=readn(); //输入
  115. len[i].u=x;
  116. len[i].v=y;
  117. add_que(x,y);
  118. add_que(y,x);
  119. }
  120. tarjan(,);
  121. int l=,r=maxlen,mid;
  122. while(l<=r) //二分答案
  123. {
  124. mid=(l+r)>>;
  125. if(check(mid))
  126. {
  127. r=mid-;
  128. ans=mid; //记录答案
  129. }
  130. else
  131. l=mid+; //不能满足则二分更大答案,以使得条件可以得到满足
  132. }
  133. printf("%d\n",ans);
  134. return ;
  135. }

NOIP2015 D2T3 运输计划的更多相关文章

  1. 【NOIP2015】运输计划

    [NOIP2015]运输计划 标签: 树上差分 LCA 二分答案 Description 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星 ...

  2. BZOJ_4326_[NOIP2015]_运输计划_(二分+LCA_树链剖分/Tarjan+差分)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=4326 给出一棵带有边权的树,以及一系列任务,任务是从树上的u点走到v点,代价为u到v路径上的权 ...

  3. UOJ #150 【NOIP2015】 运输计划

    题目描述 公元 \(2044\) 年,人类进入了宇宙纪元. \(L\) 国有 \(n\) 个星球,还有 \(n-1\) 条双向航道,每条航道建立在两个星球之间,这 \(n-1\) 条航道连通了 \(L ...

  4. 【BZOJ 4326】【NOIP2015】运输计划

    http://www.lydsy.com/JudgeOnline/problem.php?id=4326 题目描述 公元2044年,人类进入了宇宙纪元. 国有个星球,还有条双向航道,每条航道建立在两个 ...

  5. 【NOIP2015】运输计划(二分,差分)

    题面 Description 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P ...

  6. [NOIP 2015TG D2T3] 运输计划

    题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家 ...

  7. [NOIP2015 TG D2T3]运输计划

    题目大意: 给你一棵n个节点的树,有边权,有多个任务,每个要求从ui号节点到 vi号节点去.m 个计划, 这 m 个计划会同时开始.当这 m 个任务都完成时,工作完成. 现在可以把任意一个边的边权变为 ...

  8. JZYZOJ1454 NOIP2015 D2T3_运输计划 二分 差分数组 lca tarjan 树链剖分

    http://172.20.6.3/Problem_Show.asp?id=1454 从这道题我充分认识到我的脑子里好多水orz. 如果知道了这个要用二分和差分写,就没什么思考上的难点了(屁咧你写了一 ...

  9. 【NOIP2015】运输计划(树上差分,二分答案)

    题意:一棵有边权的树上有m条路径,要求选择一条边使其边权变为0,使得最大路径长度最小 n,m<=300000 思路:直接求最优方案不可做,但检验对于某一个ans是否能有方案是可行的 取出所有总长 ...

随机推荐

  1. 解题:PA 2014 Bohater

    题面 我们把怪分成两类,打完了了能回血的和打完了不能回血的,然后分开打. 对于能回血的,我们先打攻击力低的,因为如果先打一个攻击力高的显然不一定能直接打过,所以先打一些攻击力低的回回血. 对于不能回血 ...

  2. 解题:SCOI 2005 骑士精神

    题面 我把这个当做IDA*的模板题的说,说说我个人对IDA*的理解 IDA*是一个DFS,和A*一样,它也有一个乐观的估价函数.这里这个估价函数是用来限制状态的扩展的,如果当前代价加上乐观的估计都无法 ...

  3. Python高手之路【四】python函数装饰器,迭代器

    def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...

  4. P2572 [SCOI2010]序列操作

    对自己 & \(RNG\) : 骄兵必败 \(lpl\)加油! P2572 [SCOI2010]序列操作 题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要 ...

  5. Hadoop基础-HDFS分布式文件系统的存储

    Hadoop基础-HDFS分布式文件系统的存储 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.HDFS数据块 1>.磁盘中的数据块 每个磁盘都有默认的数据块大小,这个磁盘 ...

  6. Xcode关闭警告

    对于关闭某个警告,如果需要全局关闭的话,直接在Other C Flags里写 -Wno-...就行了,比如 -Wextra -Wno-sign-compare 就是一个常见的组合.如果相对某几个文件开 ...

  7. python学习笔记3-函数的递归

    递归就是指自己函数的自我调用 #递归 #自己调用自己,函数的循环 def test1(): num = int(input('please enter a number:')) if num%2==0 ...

  8. [整理]C中的静态存储区

    静态存储区:即内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.它主要存放静态数据.全局数据和常量.栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些 ...

  9. 使用jQuery仿淘宝商城多格焦点图滚动切换效果

    1.效果及功能说明 图片滚动切换特效,高仿2012淘宝商城首页多格子焦点图切换,鼠标滑过焦点图片各个格子区域聚光灯效果展示 2.实现原理 在显示div的下面有一个按钮条在鼠标触及到按钮的时候会改变那妞 ...

  10. 【转】 jquery easyui Tab 引入页面的问题

    原地址:http://blog.csdn.net/superdog007/article/details/8225518 jQuery Easyui 的tabs插件有两种方式加载某个tab(标签页)上 ...