描述


http://www.lydsy.com/JudgeOnline/problem.php?id=4326

给出一棵带有边权的树,以及一系列任务,任务是从树上的u点走到v点,代价为u到v路径上的权值之和,总代价是所有任务代价中的最大代价.现在可以将某一个边权值变为0,问总代价最小是多少.

分析


最小化最大值,显然可以二分,转化为假定解判断是否可行的问题.

那么问题就转化成了判断但前假定解t是否可行.

如何做呢?

我们先求出每一个任务的代价,这个可以用随便什么LCA算法求,然后统计出其中超过假定解t的不合法的任务.这些不合法的任务的道路上必须有某条边的权值被该成0,但是只能改一条边,那么必须改动它们的公共边,也就是这些路径的交.

那么如何求这些路径的交呢?

如果我们用cnt[i]表示i和它父亲的连边被几条这样不合法的路径经过,那么我们要找的就是cnt[i]=(不合法路径数目)的边.

这个又该怎么实现呢?一条一条路径跑?显然太慢了!

想想如果把这个问题转化成线性的,在一个线段上的话该怎么做?

线段树?ok的,在树上的话显然要用链剖了,这样的复杂度是带一个log的.

有没有更好的方法?

差分.如果[l,r]的次数要+1,我们就把l位置+1,r+1位置-1,表示给l及其之后的全部+1次,再给r+1及其之后的全部-1次.全部操作结束后从前向后扫一遍,统计前面对后面的影响即可.

我们把这样的算法搬到树上就好了.

树链剖分:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. const int maxn=+;
  5. int n,m,cnt;
  6. int f[maxn],dep[maxn],sz[maxn],son[maxn],top[maxn],d[maxn],ct[maxn],head[maxn];
  7. struct edge{
  8. int to,d,next;
  9. edge(int to=,int d=,int next=):to(to),d(d),next(next){}
  10. }g[maxn<<];
  11. struct query{
  12. int u,v,lca,l;
  13. }q[maxn];
  14. inline int read(int &x){ x=; int k=; char c; for(c=getchar();c<''||c>'';c=getchar())if(c=='-') k=-; for(;c>=''&&c<='';c=getchar()) x=x*+c-''; return x*k; }
  15. void add_edge(int u,int v,int d){
  16. g[++cnt]=edge(v,d,head[u]); head[u]=cnt;
  17. g[++cnt]=edge(u,d,head[v]); head[v]=cnt;
  18. }
  19. void dfs1(int u){
  20. sz[u]=;
  21. for(int i=head[u];i;i=g[i].next){
  22. int v=g[i].to;
  23. if(v==f[u]) continue;
  24. f[v]=u; dep[v]=dep[u]+; d[v]=d[u]+g[i].d;
  25. dfs1(v); sz[u]+=sz[v];
  26. if(sz[v]>sz[son[u]]) son[u]=v;
  27. }
  28. }
  29. void dfs2(int u){
  30. if(son[u]) top[son[u]]=top[u], dfs2(son[u]);
  31. for(int i=head[u];i;i=g[i].next){
  32. int v=g[i].to;
  33. if(v==f[u]||v==son[u]) continue;
  34. top[v]=v; dfs2(v);
  35. }
  36. }
  37. int lca(int u,int v){
  38. while(top[u]!=top[v]){
  39. if(dep[top[u]]<dep[top[v]]) swap(u,v);
  40. u=f[top[u]];
  41. }
  42. return dep[u]<dep[v]?u:v;
  43. }
  44. void update(int u){
  45. for(int i=head[u];i;i=g[i].next){
  46. int v=g[i].to;
  47. if(v==f[u]) continue;
  48. update(v);
  49. ct[u]+=ct[v];
  50. }
  51. }
  52. inline bool C(int x){
  53. int tot=,maxi=; memset(ct,,sizeof ct);
  54. for(int i=;i<=m;i++){
  55. if(q[i].l>x){
  56. tot++;
  57. ct[q[i].u]++;
  58. ct[q[i].v]++;
  59. ct[q[i].lca]-=;
  60. maxi=max(maxi,q[i].l);
  61. }
  62. }
  63. update();
  64. for(int i=;i<=n;i++)if(ct[i]==tot&&maxi-(d[i]-d[f[i]])<=x) return true;
  65. return false;
  66. }
  67. int bsearch(int l,int r){
  68. while(l<r){
  69. int mid=l+(r-l)/;
  70. if(C(mid)) r=mid;
  71. else l=mid+;
  72. }
  73. return l;
  74. }
  75. int main(){
  76. int maxi=,maxj=;
  77. read(n); read(m);
  78. for(int i=;i<n;i++){
  79. int u,v,d; read(u); read(v); read(d);
  80. add_edge(u,v,d);
  81. maxi=max(maxi,d);
  82. }
  83. dfs1(); dfs2();
  84. for(int i=;i<=m;i++){
  85. read(q[i].u); read(q[i].v);
  86. q[i].lca=lca(q[i].u,q[i].v);
  87. q[i].l=d[q[i].u]+d[q[i].v]-*d[q[i].lca];
  88. maxj=max(maxj,q[i].l);
  89. }
  90. printf("%d\n",bsearch(max(,maxj-maxi),maxj));
  91. return ;
  92. }

Tarjan:

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3.  
  4. const int maxn=+;
  5. int n,m,cnt1,cnt2;
  6. int f[maxn],p[maxn],d[maxn],head1[maxn],head2[maxn],ct[maxn];
  7. bool vis[maxn];
  8. struct edge{
  9. int to,d,next;
  10. edge(int to=,int d=,int next=):to(to),d(d),next(next){}
  11. }g[maxn<<];
  12. struct Query{
  13. int u,v,lca,l;
  14. }Q[maxn];
  15. struct query{
  16. int v,id,next;
  17. query(int v=,int id=,int next=):v(v),id(id),next(next){}
  18. }q[maxn<<];
  19. inline int read(int &x){ x=;int k=;char c;for(c=getchar();c<''||c>'';c=getchar())if(c=='-')k=-;for(;c>=''&&c<='';c=getchar())x=x*+c-'';return x*k; }
  20. inline int find(int x){ return x==f[x]?x:f[x]=find(f[x]); }
  21. void add_edge(int u,int v,int d){
  22. g[++cnt1]=edge(v,d,head1[u]); head1[u]=cnt1;
  23. g[++cnt1]=edge(u,d,head1[v]); head1[v]=cnt1;
  24. }
  25. void add_query(int u,int v,int id){
  26. q[++cnt2]=query(v,id,head2[u]); head2[u]=cnt2;
  27. q[++cnt2]=query(u,id,head2[v]); head2[v]=cnt2;
  28. }
  29. void tarjan(int u){
  30. f[u]=u; vis[u]=true;
  31. for(int i=head1[u];i;i=g[i].next){
  32. int v=g[i].to; if(v==p[u]) continue;
  33. p[v]=u; d[v]=d[u]+g[i].d;
  34. tarjan(v); f[v]=u;
  35. }
  36. for(int i=head2[u];i;i=q[i].next)if(vis[q[i].v]) Q[q[i].id].lca=find(q[i].v),Q[q[i].id].l=d[u]+d[q[i].v]-*d[Q[q[i].id].lca];
  37. }
  38. void update(int u){
  39. for(int i=head1[u];i;i=g[i].next){
  40. int v=g[i].to; if(v==p[u]) continue;
  41. update(v); ct[u]+=ct[v];
  42. }
  43. }
  44. inline bool C(int x){
  45. int tot=,maxi=; memset(ct,,sizeof ct);
  46. for(int i=;i<=m;i++)if(Q[i].l>x){
  47. tot++; maxi=max(maxi,Q[i].l);
  48. ct[Q[i].u]++; ct[Q[i].v]++; ct[Q[i].lca]-=;
  49. }
  50. update();
  51. for(int i=;i<=n;i++)if(ct[i]==tot&&maxi-(d[i]-d[p[i]])<=x) return true;
  52. return false;
  53. }
  54. int bsearch(int l,int r){
  55. while(l<r){
  56. int mid=l+(r-l)/;
  57. if(C(mid)) r=mid;
  58. else l=mid+;
  59. }
  60. return l;
  61. }
  62. int main(){
  63. read(n); read(m);
  64. int maxi=,maxj=;
  65. for(int i=;i<n;i++){
  66. int u,v,d; read(u); read(v); read(d);
  67. add_edge(u,v,d);
  68. maxi=max(maxi,d);
  69. }
  70. for(int i=;i<=m;i++){
  71. read(Q[i].u); read(Q[i].v);
  72. add_query(Q[i].u,Q[i].v,i);
  73. }
  74. tarjan();
  75. for(int i=;i<=m;i++) maxj=max(maxj,Q[i].l);
  76. printf("%d\n",bsearch(max(,maxj-maxi),maxj));
  77. return ;
  78. }

4326: NOIP2015 运输计划

Time Limit: 30 Sec  Memory Limit: 128 MB
Submit: 538  Solved: 368
[Submit][Status][Discuss]

Description


元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L
国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到
vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为
tj,并且任意两艘飞船之间不会产生任何干扰。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P
把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m
个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P
可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

Input

第一行包括两个正整数 n,m,表示
L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i
行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证
1≤ai,bi≤n 且 0≤ti≤1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j
个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n

Output

输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

Sample Input

6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5

Sample Output

11

HINT

将第 1 条航道改造成虫洞: 则三个计划耗时分别为:11,12,11,故需要花费的时间为 12。
将第 2 条航道改造成虫洞: 则三个计划耗时分别为:7,15,11,故需要花费的时间为 15。
将第 3 条航道改造成虫洞: 则三个计划耗时分别为:4,8,11,故需要花费的时间为 11。
将第 4 条航道改造成虫洞: 则三个计划耗时分别为:11,15,5,故需要花费的时间为 15。
将第 5 条航道改造成虫洞: 则三个计划耗时分别为:11,10,6,故需要花费的时间为 11。
故将第 3 条或第 5 条航道改造成虫洞均可使得完成阶段性工作的耗时最短,需要花费的时间为 11。

BZOJ_4326_[NOIP2015]_运输计划_(二分+LCA_树链剖分/Tarjan+差分)的更多相关文章

  1. cogs 2109. [NOIP 2015] 运输计划 提高组Day2T3 树链剖分求LCA 二分答案 差分

    2109. [NOIP 2015] 运输计划 ★★★☆   输入文件:transport.in   输出文件:transport.out   简单对比时间限制:3 s   内存限制:256 MB [题 ...

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

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

  3. 【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

    4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status] ...

  4. bzoj 4326: NOIP2015 运输计划(二分+树链剖分)

    传送门 题解: 树链剖分快速求解任意两点间的路径的权值和: 然后,二分答案: 此题的难点是如何快速求解重合路径? 差分数组可以否??? 在此之前先介绍一下相关变量: int fa[maxn]; int ...

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

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

  6. NOIP2015 运输计划 - 二分 + 树链剖分 / (倍增 + 差分)

    BZOJ CodeVS Uoj 题目大意: 给一个n个点的边带权树,给定m条链,你可以选择树中的任意一条边,将它置为0,使得最长的链长最短. 题目分析: 最小化最大值,二分. 二分最短长度mid,将图 ...

  7. 【NOIP2015】运输计划

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

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

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

  9. NOIP 2015 BZOJ 4326 运输计划 (树链剖分+二分)

    Description 公元 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n− 条双向航道,每条航道建立在两个星球之间,这 n− 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司, ...

随机推荐

  1. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  2. java nio使用方法(转)

    最近由于工作关系要做一些Java方面的开发,其中最重要的一块就是Java NIO(New I/O),尽管很早以前了解过一些,但并没有认真去看过它的实现原理,也没有机会在工作中使用,这次也好重新研究一下 ...

  3. Linux – RedHat7 / CentOS 7 忘记root密码修改

    1.(a) 开机出现grub boot loader 开机选项菜单时,立即点击键盘任意鍵,boot loader 会暂停. (b) 按下’e’,编辑选项菜单(c) 移动上下鍵至linux16 核心命令 ...

  4. apply()与call()的区别

    一直都没太明白apply()与call()的具体使用原理,今日闲来无事,决定好好研究一番. JavaScript中的每一个Function对象都有一个apply()方法和一个call()方法,它们的语 ...

  5. GAC write failed when upgrade with InstallShield

    在接近final-build的时候,突然发现当前版本从上一个版本upgrade的时候,需要写到GAC (Global Assembly Cache)的assambly会写失败掉.但是只会在特定的Mic ...

  6. Lucas定理的理解与应用

    Lucas定理:用于计算组合数模除素数后的值,其实就是把(n,m)分别表示为p进制,累乘各位的可能取的个数,得到最终的结果: 推论:(n & m) == m则C(n,m)为奇数:即C(n,m) ...

  7. qt 5 小练习 创建无边框界面

    我们大家都知道QT5 自带的界面不是那么美观,并且每个软件我们都发现他们的边框是自定义的,所以我决定写一篇这样的博文,也许已经有许许多多篇大牛写的论文了,但我还是想写一篇记录自己的学习QT的历程 首先 ...

  8. Python创建list

    Python内置的一种数据类型是列表:list.list是一种有序的集合,可以随时添加和删除其中的元素. 比如,列出班里所有同学的名字,就可以用一个list表示: >>> ['Mic ...

  9. css3 Transition动画执行时有可能会出现闪烁的bug

    css3 Transition动画执行时有可能会出现闪烁的bug,一般出现在开始的时候. 解决方法: 1.-webkit-backface-visibility: hidden; 2.-webkit- ...

  10. Matlab使用心得

    1..*和*的区别 .*只能用于两个同型矩阵相乘,且是相对应的元素做乘法运算,其运算规则和我们线性代数里的乘法规则是不一样的:而*用于两个矩阵相乘,如mxn,nxk两个矩阵相乘,它的运算规则和线性代数 ...