记得是9月月赛题,当时做的时候觉得跟ZJOI2015幻想乡战略游戏那道题很像???,就写了,然后就写挂了。。。

我们发现假设当前点为根,我们算出\(m\)次询问中最远的\(a\)对点,如果这\(a\)对点,全部都两个点在根的不同子树中。当前点就是最优的就是答案。当全部\(a\)对点都在一个子树中,我们把答案改为那个子树对应的儿子,答案会变优。当有几对点在一个子树,另外几对点在另外的子树中,当前答案还是最优的。

所以本题的一个想法就是,一个一个的改变根使答案变优。

但是上述想法要求我们每一次移动一个点都要遍历整棵树。是在太慢了。

我们考虑用点分治的方法优化算法。当全部\(a\)对点都在一个子树中时,一个更优的答案在那个子树中,我们找到这个子树的重心当作根。这样最多遍历\(logn\)次。把复杂度变为了\(O(mlogn)\)。至此本题得到完美解决。

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<cmath>
  5. #include<algorithm>
  6. using namespace std;
  7. const int N=101000;
  8. int cnt,head[N];
  9. int g[N],size[N],all,root,dis[N],ans1[N],ans2[N],dep[N],fa[N][25],vis[N];
  10. int n,m,a[N],b[N],ans;
  11. struct edge{
  12. int to,nxt,w;
  13. }e[N*2];
  14. void add_edge(int u,int v,int w){
  15. cnt++;
  16. e[cnt].nxt=head[u];
  17. e[cnt].to=v;
  18. e[cnt].w=w;
  19. head[u]=cnt;
  20. }
  21. int read(){
  22. int sum=0,f=1;char ch=getchar();
  23. while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
  24. while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
  25. return sum*f;
  26. }
  27. void getroot(int u,int f){
  28. g[u]=0;size[u]=1;
  29. for(int i=head[u];i;i=e[i].nxt){
  30. int v=e[i].to;
  31. if(vis[v]||f==v)continue;
  32. getroot(v,u);
  33. size[u]+=size[v];
  34. g[u]=max(g[u],size[v]);
  35. }
  36. g[u]=max(g[u],all-g[u]);
  37. if(g[u]<g[root])root=u;
  38. }
  39. void getdis(int u,int f,int w){
  40. dep[u]=dep[f]+1;
  41. fa[u][0]=f;
  42. dis[u]=w;
  43. for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
  44. for(int i=head[u];i;i=e[i].nxt){
  45. int v=e[i].to;
  46. if(v==f)continue;
  47. getdis(v,u,w+e[i].w);
  48. }
  49. }
  50. int getlca(int x,int y){
  51. if(dep[x]<dep[y])swap(x,y);
  52. for(int i=20;i>=0;i--)
  53. if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
  54. if(x==y)return x;
  55. for(int i=20;i>=0;i--)
  56. if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
  57. return fa[x][0];
  58. }
  59. int up(int u,int x){
  60. for(int i=20;i>=0;i--)
  61. if((x>>i)&1)u=fa[u][i],x-=(1<<i);
  62. return u;
  63. }
  64. int calc(int u){
  65. getdis(u,0,0);
  66. int tmp=0;
  67. for(int i=1;i<=m;i++){
  68. int x=dis[a[i]]+dis[b[i]];
  69. if(x>tmp){
  70. cnt=0;
  71. ans1[++cnt]=a[i];ans2[cnt]=b[i];
  72. tmp=x;
  73. }
  74. }
  75. ans=min(ans,tmp);
  76. tmp=0;
  77. for(int i=1;i<=cnt;i++){
  78. int x=up(ans1[i],dep[ans1[i]]-dep[u]-1);
  79. int y=up(ans2[i],dep[ans2[i]]-dep[u]-1);
  80. if(x==y){
  81. if(tmp==0)tmp=x;
  82. else return -1;
  83. }
  84. }
  85. if(tmp==-1)return -1;
  86. return tmp;
  87. }
  88. void work(int u){
  89. int x=calc(u);
  90. if(x==-1)return;vis[u]=1;
  91. for(int i=head[u];i;i=e[i].nxt){
  92. int v=e[i].to;
  93. if(vis[v])continue;
  94. if(v==x){
  95. root=0;all=size[v];
  96. getroot(v,0);
  97. work(root);
  98. }
  99. }
  100. }
  101. int main(){
  102. n=read();m=read();
  103. for(int i=1;i<n;i++){
  104. int u=read(),v=read(),w=read();
  105. add_edge(u,v,w);add_edge(v,u,w);
  106. }
  107. for(int i=1;i<=m;i++)a[i]=read(),b[i]=read();
  108. ans=1e9;
  109. g[0]=n+10,all=n;
  110. getroot(1,0);work(root);
  111. printf("%d",ans);
  112. return 0;
  113. }

luogub P4886 快递员(点分治)的更多相关文章

  1. 【题解】P4886快递员

    [题解]P4886 快递员 淀粉质好题!!!加深了我对点分治的理解.最近分治学了好多啊. 题目大意 给定你一颗有边权的树,再给你\(m\)和点对,请你在树上选出来一个点,使得所有点对到这个点的距离的最 ...

  2. [P4886] 快递员

    考虑在树上选个点rt作为根,并且快递中心就选这儿.计算出所有配送的代价(2*两段之和),设他们的最大值为Max.若此时存在下列情况时,可以判定Max已经为最优解. 1)存在代价为Max的配送(u,v) ...

  3. Luogu4886 快递员 点分治

    传送门 淀粉质好题啊qaq 我们先考虑随便选择一个点作为邮递中心,通过移动邮递中心找到更优的位置.将路径最大值求出,并将路径最大值对应的那一些路径拿出来考虑.可以知道,如果说这些路径中存在一条经过当前 ...

  4. [洛谷P4886]快递员

    题目大意:一个$n$个点的树,树上有$m$个点对$(a,b)$,找到一个点$x$,使得$max(dis(x,a_i)+dis(x,b_i))$最小 如果做过幻想乡的战略游戏这道题,应该这道题的思路一眼 ...

  5. [luogu4886] 快递员(点分治,树链剖分,lca)

    dwq推的火题啊. 这题应该不算是点分治,但是用的点分治的思想. 每次找重心,算出每一对询问的答案找到答案最大值,考虑移动答案点,使得最大值减小. 由于这些点一定不能在u的两颗不同的子树里,否则你怎么 ...

  6. 【洛谷 P4886】 快递员 (点分治)

    这题因为一些小细节还是\(debug\)了很久...不过我第一次用脚本对拍,不亏. 先随便找一个点作为根,算出答案,即所有点对到这个点的距离和的最大值,并记录所有距离最大的点对.如果这个点在任意一个距 ...

  7. 一篇自己都看不懂的点分治&点分树学习笔记

    淀粉质点分治可真是个好东西 Part A.点分治 众所周知,树上分治算法有$3$种:点分治.边分治.链分治(最后一个似乎就是树链剖分),它们名字的不同是由于分治方式的不同的.点分治,顾名思义,每一次选 ...

  8. [bzoj2152][聪聪和可可] (点分治+概率)

    Description 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好 ...

  9. POJ 2965. The Pilots Brothers' refrigerator 枚举or爆搜or分治

    The Pilots Brothers' refrigerator Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22286 ...

随机推荐

  1. Unity 围绕X、Y、Z旋转图例

    绿色:绕X 红色:绕Y 蓝色:绕Z PS:这是右手坐标系,Unity为左手坐标系 不知道啥叫左手右手?参见我的另一篇文章http://www.cnblogs.com/36bian/p/7571727. ...

  2. css——样式的优先级

    样式的优先级 在p中有id,class,标签,行内样式,它们的优先级: 1.id 样式>class样式>标签样式 2.行内样式>内嵌样式>外部样式 强制优先级 比如我希望上面的 ...

  3. 使用jq把js代码封装一个自己的插件

    为什么要把js功能封装成插件呢?我觉得有以下几点吧 1.最基本的原因就是便于代码复用. 2.便于维护和管理. 3.提升自身的能力. 4.避免各个相同功能组件的干扰,以及一些作用域会相互影响的问题. j ...

  4. CF1037E Trips (离线+图上构造)

    题目大意:一共有n个人,每天早上会有两个人成为朋友,朋友关系不具有传递性,晚上,它们会组织旅游,如果一个人去旅游,那么他不少于$k$个朋友也要和他去旅游,求每天的最大旅游人数 一开始并没有想到反向建图 ...

  5. 拷贝构造函数不能传值,只能传引用,而且一般是传const引用

    为什么呢?因为传值函数,需要调用拷贝构造函数,那就层层循环无止境了.

  6. HDU 2475 Box 树型转线型 + 伸展树

    树型转线型.第一次听说这个概念. . . , 可是曾经已经接触过了,如LCA的预处理部分和树链剖分等.可是没想到还能这么用,三者虽说有不同可是大体思想还是非常相近的,学习了. 推荐博客http://b ...

  7. 使用Love2D引擎开发贪吃蛇游戏

    今天来介绍博主近期捣腾的一个小游戏[贪吃蛇],贪吃蛇这个游戏相信大家都不会感到陌生吧.今天博主将通过Love2D这款游戏引擎来为大家实现一个简单的贪吃蛇游戏,在本篇文章其中我们将会涉及到贪吃蛇的基本算 ...

  8. 第十七章_Web注解

    1.HandlesTypes 这个注解类型用来声明ServletContainerInitializer能够处理哪些类型的类.它有一个属性.一个值.用来声明类的类型.比如,以下的ServletCont ...

  9. 30个php操作redis经常用法代码样例

    这篇文章主要介绍了30个php操作redis经常用法代码样例,本文事实上不止30个方法,能够操作string类型.list类型和set类型的数据,须要的朋友能够參考下 redis的操作非常多的,曾经看 ...

  10. Mysql实战45讲 04讲深入浅出索引(上)读书笔记 极客时间

    极客时间 Mysql实战45讲 04讲深入浅出索引 极客时间(上)读书笔记  笔记体悟 1.索引的作用:提高数据查询效率2.常见索引模型:哈希表.有序数组.搜索树3.哈希表:键 - 值(key - v ...