基本思路:

首先按照weightA升序排序,然后依次在图中加边,并维护起点到终点路径上weightB的最大值

如果加边过程中生成了环,则删除环中weightB最大的边

由于是无向图,点之间没有拓扑序,所以在建立LCT模型时,可以将原图的边也视为点,这样就转化成了维护路径上的点权最大值(Orz Hzwer)

点的连通性可以用并查集维护

AC code:(其实Splay双旋一次时只需要进行3次update,而代码中舍弃了这个优化)

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <algorithm>
  4.  
  5. ;
  6. ;
  7. const int inf=0x3f3f3f3f;
  8.  
  9. struct Edge
  10. {
  11. int u,v;
  12. int weightA,weightB;
  13.  
  14. void assign(int _u,int _v,int _wa,int _wb)
  15. {
  16. u=_u; v=_v; weightA=_wa; weightB=_wb;
  17. }
  18. bool operator < (const Edge& other) const
  19. {
  20. return this->weightA < other.weightA;
  21. }
  22. };
  23.  
  24. Edge elist[maxM];
  25.  
  26. inline int getMaxIdx(int u,int v)
  27. {
  28. return elist[u].weightB > elist[v].weightB ? u : v;
  29. } //Let elist[0].weightB = -inf
  30.  
  31. struct Splay
  32. {
  33. int idx;
  34. int maxIdx;
  35. bool rev;
  36. Splay* child[];
  37. Splay* parent;
  38.  
  39. void init(int id)
  40. {
  41. idx=maxIdx=id;
  42. rev=false;
  43. child[]=child[]=parent=;
  44. }
  45. bool isRoot()
  46. {
  47. if(!parent) return true;
  48. ] && ];
  49. }
  50. void update()
  51. {
  52. maxIdx=idx;
  53. ]) maxIdx=getMaxIdx(]->maxIdx);
  54. ]) maxIdx=getMaxIdx(]->maxIdx);
  55. }
  56. void reverse()
  57. {
  58. if(!isRoot()) parent->reverse();
  59. if(rev) {
  60. std::swap(child[],child[]);
  61. ]) child[]->rev ^= ;
  62. ]) child[]->rev ^= ;
  63. ;
  64. }
  65. }
  66. ; }
  67. void rotate(int dir)
  68. {
  69. Splay* temp=parent;
  70. this->parent=temp->parent;
  71. if(parent)
  72. {
  73. ]) parent->child[]=this;
  74. ]) parent->child[]=this;
  75. }
  76. temp->child[dir^]=this->child[dir];
  77. if(child[dir]) child[dir]->parent=temp;
  78. child[dir]=temp;
  79. temp->parent=this;
  80. temp->update();
  81. this->update();
  82. }
  83. void splay()
  84. {
  85. reverse();
  86. while(!isRoot())
  87. {
  88. );
  89. ]) st|=;
  90. ;
  91. if(!parent->isRoot())
  92. {
  93. ]) st|=;
  94. ;
  95. }
  96. switch(st)
  97. {
  98. : rotate(); break;
  99. : rotate(); break;
  100. : parent->rotate(); ); break;
  101. : rotate(); rotate(); break;
  102. : rotate(); rotate(); break;
  103. :parent->rotate(); ); break;
  104. }
  105. }
  106. }
  107. };
  108.  
  109. Splay node[maxN+maxM];
  110.  
  111. int access(int idx)
  112. {
  113. Splay *cur,*last;
  114. ;cur;last=cur,cur=cur->parent)
  115. {
  116. cur->splay();
  117. cur->child[]=last;
  118. cur->update();
  119. }
  120. return last->maxIdx;
  121. }
  122. inline void setAsRoot(int idx)
  123. {
  124. access(idx);
  125. node[idx].splay();
  126. node[idx].setRev();
  127. }
  128. inline void link(int u,int v)
  129. {
  130. setAsRoot(u);
  131. node[u].parent=node+v;
  132. }
  133. inline void cut(int u,int v)
  134. {
  135. setAsRoot(u);
  136. access(v);
  137. node[v].splay();
  138. node[v].child[]=node[u].parent=;
  139. node[v].update();
  140. }
  141. inline int query(int u,int v)
  142. {
  143. setAsRoot(u);
  144. return access(v);
  145. }
  146.  
  147. int N,M;
  148. int center[maxN];
  149.  
  150. int getCenter(int idx)
  151. {
  152. return center[idx]==idx ? idx :
  153. center[idx]=getCenter(center[idx]);
  154. }
  155. void input()
  156. {
  157. scanf("%d%d",&N,&M);
  158. int u,v,wa,wb;
  159. ;i<=M;i++)
  160. {
  161. scanf("%d%d%d%d",&u,&v,&wa,&wb);
  162. if(u==v) { --i; --M; }
  163. else elist[i].assign(u,v,wa,wb);
  164. }
  165. }
  166. void init()
  167. {
  168. elist[].weightB=-inf;
  169. std::sort(elist+,elist+M+);
  170. ;i<=M;i++) node[i].init(i);
  171. ;i<=N;i++) node[i+M].init();
  172. ;i<=N;i++) center[i]=i;
  173. }
  174. void addEdge(int e)
  175. {
  176. int& u=elist[e].u;
  177. int& v=elist[e].v;
  178. int cu=getCenter(u);
  179. int cv=getCenter(v);
  180. if(cu!=cv)
  181. {
  182. link(e,M+u);
  183. link(e,M+v);
  184. center[cu]=cv;
  185. }
  186. else {
  187. int mx=query(M+u,M+v);
  188. if(elist[e].weightB < elist[mx].weightB)
  189. {
  190. cut(mx,M+elist[mx].u);
  191. cut(mx,M+elist[mx].v);
  192. link(e,M+u);
  193. link(e,M+v);
  194. }
  195. }
  196. }
  197. int solve()
  198. {
  199. int ans(inf);
  200. init();
  201. ;i<=M;i++)
  202. {
  203. addEdge(i);
  204. )==getCenter(N))
  205. {
  206. ,M+N);
  207. ans=std::min(ans,elist[i].weightA+elist[mx].weightB);
  208. }
  209. }
  210. :ans;
  211. }
  212.  
  213. int main()
  214. {
  215. input();
  216. printf("%d\n",solve());
  217. ;
  218. }

——————分割线——————

这道题我Debug了2天共计5h,最后偶然间查明了死因居然是——

  1. #ifdef WRONG_SPLAY_CODE
  2. void Splay::splay()
  3. {
  4. reverse();
  5. while(!isRoot())
  6. {
  7. );
  8. ]) st|=;
  9. ;
  10. if(!parent->isRoot())
  11. {
  12. ]) st|=;
  13. ;
  14. }
  15. switch(st)
  16. {
  17. : rotate(); break;
  18. : rotate(); break;
  19. : parent->rotate(); ); break;
  20. : rotate(); rotate(); break;
  21. : rotate(); rotate(); break;
  22. :parent->rotate(); ); break;
  23. }
  24. }
  25. }
  26. #endif

I FELL COLLAPSED BECAUSE OF THIS

不过在Debug期间,参考了不少其他神犇的AC代码,也学到了各种姿势,算是因祸得福吧……

Vijos1865 NOI2014 魔法森林 LCT维护生成树的更多相关文章

  1. P2387 [NOI2014]魔法森林 LCT维护最小生成树

    \(\color{#0066ff}{ 题目描述 }\) 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 ...

  2. 【BZOJ 3669】 [Noi2014]魔法森林 LCT维护动态最小生成树

    这道题看题意是在求一个二维最小瓶颈路,唯一可行方案就是枚举一维在这一维满足的条件下使另一维最小,那么我们就把第一维排序利用A小的边在A大的情况下仍成立来动态加边维护最小生成树. #include &l ...

  3. [Luogu P2387] [NOI2014]魔法森林 (LCT维护边权)

    题面 传送门:https://www.luogu.org/problemnew/show/P2387 Solution 这题的思想挺好的. 对于这种最大值最小类的问题,很自然的可以想到二分答案.很不幸 ...

  4. BZOJ 3669: [Noi2014]魔法森林( LCT )

    排序搞掉一维, 然后就用LCT维护加边MST. O(NlogN) ------------------------------------------------------------------- ...

  5. bzoj 3669: [Noi2014]魔法森林 (LCT)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3669 题面: 3669: [Noi2014]魔法森林 Time Limit: 30 Sec  ...

  6. [NOI2014]魔法森林 LCT

    题面 [NOI2014]魔法森林 题解 一条路径的代价为路径上的\(max(a[i]) + max(b[i])\),因为一条边同时有$a[i], b[i]$2种权值,直接处理不好同时兼顾到,所以我们考 ...

  7. bzoj3669: [Noi2014]魔法森林 lct版

    先上题目 bzoj3669: [Noi2014]魔法森林 这道题首先每一条边都有一个a,b 我们按a从小到大排序 每次将一条路劲入队 当然这道题权在边上 所以我们将边化为点去连接他的两个端点 当然某两 ...

  8. loj2245 [NOI2014]魔法森林 LCT

    [NOI2014]魔法森林 链接 loj 思路 a排序,b做动态最小生成树. 把边拆成点就可以了. uoj98.也许lct复杂度写假了..越卡常,越慢 代码 #include <bits/std ...

  9. bzoj 3669: [Noi2014] 魔法森林 LCT版

    Description 为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士.魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M.初始时小E同学在号节 ...

随机推荐

  1. Linux Shell编程(13)——数字常量

    除非一个数字有特别的前缀或符号,否则shell脚本把它当成十进制的数.一个前缀为0的数字是八进制数.一个前缀为0x的数字是十六进制数.一个数用内嵌的#来求值则看成BASE#NUMBER(有范围和符号限 ...

  2. Qt 与 JavaScript 通信

    使用QWebView加载网页后,解决Qt与JavaScript通信的问题: The QtWebKit Bridge :http://qt-project.org/doc/qt-4.8/qtwebkit ...

  3. 二:java语法基础:

    1,关键字:其实就是某种语言赋予了特殊含义的单词. 保留字:其实就是还没有赋予特殊含义,但是准备日后要使用过的单词. 2,标示符:其实就是在程序中自定义的名词.比如类名,变量名,函数名.包含 0-9. ...

  4. HDOJ1518Square 深搜

    Square Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  5. Show All Running Processes in Linux

    ps由于历史的原因,所以很奇特,有些命令必须加"-",比如: ps A 上面的写法是错误的 ********* simple selection ********* ******* ...

  6. Android开发学习之Adapter

    Adapter是指适配器的意思,在Android中,适配器扮演者重要的角色,是UI与Data实现绑定的一个桥梁.Adapter负责创建和显示每个项目的子View和提供对下层数据的访问.支持Adapte ...

  7. winform DataGridView双击修改单元格的值 分类: DataGridView 2014-08-04 19:39 150人阅读 评论(0) 收藏

    定义全局变量 string abcmycode = "";//当前行自编号 bool tf = false;//是否双击 //双击可编辑         private void ...

  8. Sencha Touch 和 jQuery Mobile 的比较

    Sencha Touch 和 jQuery Mobile 的比较 英文原文:Sencha Touch vs jQuery Mobile 标签: Sencha Touch jQuery Mobile 1 ...

  9. cocos2d&amp;cocos2dx学习资源

    汇总一下自己学习Cocos2d和cocos2dx认为比較好的一些资源: 书籍: <iPhone&iPad cocos2d游戏开发实战> Steffen Itterheim < ...

  10. 支持MySql的数据库自动分表工具DBShardTools发布

    支持MySql的数据库自动分表工具DBShardTools发布 前段时间参与了公司的一个项目,这个项目的特点是数据量.访问量都比较大,考虑使用数据库水平分表策略,Google了大半天,竟然没有找到分表 ...