如果不是uoj上有的话(听说这是China Round),我有可能就错过这道题目了(这是我有史以来为oi写的最长的代码,用了我一天TAT!)。

题目

传送门

一个连通无向图,点上有权,支持两种操作:

  • 修改某点的权值
  • 询问点\(x\)到\(y\)的经过的最小的点(同一个点不能重复经过)

算法

这题想起来是不难的:

容易想到做一个强连通分量,每个分量里的点可以互相到达。

这时会有一个猜想:在一个分量里,是不是任意指定三点\(a,b,c\),都有一条合法路径从\(a\)到\(b\)途中经过\(c\)呢。我当时想了想,也没严谨证明,觉得是对的,现在做完了觉得有必要证明一下,然后我就在解答里找到了这个:

Consider a biconnected graph with at least 3 vertices. If we remove any vertex or any edge, the graph is still connected.

We build a network on the graph. Let's use (u,v,w) to describe a directed edge from u to v with capacity w. For each edge (u,v) of the original graph, we build (u,v,1) and (v,u,1). Build (S,c,2), (a,T,1) and (b,T,1). For each vertex other than S,T,c, we should give a capacity of 1 to the vertex.

In order to give capacity to vertex u, we build two vertices u1,u2 instead of u. For each (v,u,w), build (v,u1,w). For each (u,v,w), build(u2,v,w). Finally build (u1,u2,1).

Hence, if the maximal flow from S to T is 2, there is a simple path from a to b going through c.

Now we consider the minimal cut of the network. It is easy to find that minimal cut <= 2, so let's prove minimal cut > 1, which means, no matter which edge of capacity 1 we cut, there is still a path from S to T.

If we cut an edge like (u1,u2,1), it is equivalent to set the capacity of the vertex to 0, and equivalent to remove the vertex from the original graph. The graph is still connected, so there is still a path in the network.

If we cut other edges, it is equivalent to remove an edge from the original graph. It is still connected, too.

Now we have minimal cut > 1, which means maximal flow = minimal cut = 2. So there is always a simple path from a to b going through c.

这个证明不走寻常路,通过最小割来证明,服了!

这样子我们就可以将每个分量看作一个点生成一棵树,询问只需要做一个LCA,修改就用一个堆维护。因为有修改操作,所以就把LCA改为树链剖分。

问题在于,一点可能在许多个分量里,那么修改时需要修改很多堆,于是,可以把每个割点的值仅仅维护在它的父亲分量(这是相对于深度优先搜索树而言),不过这样询问时要记得特殊处理LCA的值,就是说它们的LCA有一个割点的信息在它们LCA的父亲上。构图类似下面的。

注:右边的每个点都是一个分量,并且里面的信息用堆维护。这里的连线是对应的点。点的颜色表示,左边某色的所有点的信息储存在右边相同颜色的点上。

也许你已经注意到了,有一个特殊地方,如果\(a\)和\(b\)相连,那么这里可以就把它们当作一个分量。并且为了写程序方便,故意在根结点的顶部多加了一个点。

代码

有两个很坑的地方(对于我来说):

  • 不要用set而是multiset
  • dfs子结点\(u\)后,不要漏了这一句(太久没写tarjan了):low[v] = min(low[v], low[u])

5.4k(已经过滤掉了0.8k的调试信息)

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <stack>
  6. #include <set>
  7. using namespace std;
  8. template <class t>
  9. void tension(t &a, const t &b) {
  10. if (b < a) {
  11. a = b;
  12. }
  13. }
  14. template <class t>
  15. void relax(t &a, const t &b) {
  16. if (b > a) {
  17. a = b;
  18. }
  19. }
  20. const int INF = 0x3f3f3f3f;
  21. const int MAXN = (int) 1e5 + 3;
  22. const int MAXM = (int) 1e5 + 3;
  23. const int MAXSIZE = 1 << 19;
  24. int n, m, Q, A[MAXN];
  25. struct Edge {
  26. int to;
  27. Edge *next;
  28. };
  29. struct BCC {
  30. multiset<int> s;
  31. void insert(int);
  32. int maxval;
  33. BCC() {
  34. maxval = INF;
  35. }
  36. void change(int a, int b) {
  37. s.erase(s.find(a));
  38. s.insert(b);
  39. maxval = *s.begin();
  40. }
  41. };
  42. int cntBcc;
  43. BCC bcc[MAXN * 2];
  44. int idxNew[MAXN];
  45. void BCC::insert(int x) {
  46. s.insert(A[x]);
  47. maxval = *s.begin();
  48. }
  49. namespace zkwSegmentTree {
  50. int depth;
  51. int A[MAXSIZE + 1];
  52. void init(int size) {
  53. while ((1 << depth) - 2 < size) {
  54. depth ++;
  55. }
  56. }
  57. void initModify(int x, int v) {
  58. A[(1 << depth) + x] = v;
  59. }
  60. void upgrade() {
  61. for (int i = (1 << depth) - 1; i; i --)
  62. A[i] = min(A[i << 1], A[i << 1 ^ 1]);
  63. }
  64. int query(int a, int b) {
  65. a = (1 << depth) + a - 1;
  66. b = (1 << depth) + b + 1;
  67. int ans = INF;
  68. for (; a ^ b ^ 1; a >>= 1, b >>= 1) {
  69. if (~a & 1) tension(ans, A[a ^ 1]);
  70. if ( b & 1) tension(ans, A[b ^ 1]);
  71. }
  72. return ans;
  73. }
  74. void modify(int a, int v)
  75. {
  76. a = (1 << depth) + a;
  77. A[a] = v;
  78. for (a >>= 1; a; a >>= 1)
  79. A[a] = min(A[a << 1], A[a << 1 ^ 1]);
  80. }
  81. }
  82. namespace newGraph {
  83. const int MAXNODE = MAXN * 2;
  84. int root;
  85. Edge *info[MAXNODE], mem[MAXNODE], *cur_mem = mem;
  86. int fa[MAXNODE];
  87. void insert(int f, int s) {
  88. cur_mem->to = s;
  89. cur_mem->next = info[f];
  90. info[f] = cur_mem ++;
  91. }
  92. int cntPath;
  93. int top[MAXNODE], heavySon[MAXNODE];
  94. int dep[MAXNODE], size[MAXNODE], belong[MAXNODE];
  95. int idxMap[MAXNODE];
  96. int cutVertex[MAXNODE];
  97. void cutLightHeavy() {
  98. static int que[MAXNODE];
  99. int low = 0, high = 0;
  100. que[high ++] = root;
  101. fa[root] = -1;
  102. while (low < high) {
  103. int v = que[low ++];
  104. for (Edge *pt = info[v]; pt; pt = pt->next) {
  105. int u = pt->to;
  106. que[high ++] = u;
  107. dep[u] = dep[v] + 1;
  108. fa[u] = v;
  109. }
  110. }
  111. for (int i = high - 1; i >= 0; i --) {
  112. int v = que[i];
  113. size[v] = 1;
  114. heavySon[v] = -1;
  115. for (Edge *pt = info[v]; pt; pt = pt->next) {
  116. int u = pt->to;
  117. size[v] += size[u];
  118. if (heavySon[v] == -1 || size[u] > size[heavySon[v]]) {
  119. heavySon[v] = u;
  120. }
  121. }
  122. }
  123. zkwSegmentTree::init(cntBcc);
  124. int cntIdx = 1;
  125. for (int i = 0; i < high; i ++) {
  126. int v = que[i];
  127. if (idxMap[v] == 0) {
  128. top[cntPath] = v;
  129. for (; v != -1; v = heavySon[v]) {
  130. idxMap[v] = cntIdx ++;
  131. zkwSegmentTree::initModify(idxMap[v], bcc[v].maxval);
  132. belong[v] = cntPath;
  133. }
  134. cntPath ++;
  135. }
  136. }
  137. zkwSegmentTree::upgrade();
  138. }
  139. int query(int a, int b) {
  140. int ret = INF;
  141. a = idxNew[a], b = idxNew[b];
  142. while (belong[a] != belong[b]) {
  143. if (dep[top[belong[a]]] < dep[top[belong[b]]]) {
  144. swap(a, b);
  145. }
  146. tension(ret, zkwSegmentTree::query(idxMap[top[belong[a]]], idxMap[a]));
  147. a = fa[top[belong[a]]];
  148. }
  149. if (dep[a] < dep[b]) swap(a, b);
  150. tension(ret, zkwSegmentTree::query(idxMap[b], idxMap[a]));
  151. tension(ret, A[cutVertex[b]]);
  152. return ret;
  153. }
  154. }
  155. namespace srcGraph {
  156. Edge *info[MAXN], mem[MAXM * 2], *cur_mem = mem;
  157. void insert2(int a, int b) {
  158. cur_mem->to = b;
  159. cur_mem->next = info[a];
  160. info[a] = cur_mem ++;
  161. cur_mem->to = a;
  162. cur_mem->next = info[b];
  163. info[b] = cur_mem ++;
  164. }
  165. void init() {
  166. for (int i = 0; i < m; i ++) {
  167. int a, b;
  168. scanf("%d%d\n", &a, &b);
  169. insert2(a, b);
  170. }
  171. }
  172. int dfn[MAXN], low[MAXN], bccFa[MAXN];
  173. void make() {
  174. stack< pair<int, Edge*> > stk;
  175. stk.push(make_pair(1, (Edge*) NULL) );
  176. stack<int> contain;
  177. memset(dfn, -1, sizeof(dfn));
  178. memset(low, -1, sizeof(low));
  179. memset(bccFa, -1, sizeof(bccFa));
  180. int cntDfn = 0;
  181. while (! stk.empty()) {
  182. int v = stk.top().first;
  183. Edge *&pt = stk.top().second;
  184. if (! pt) {
  185. dfn[v] = low[v] = cntDfn ++;
  186. pt = info[v];
  187. contain.push(v);
  188. }
  189. else {
  190. int u = pt->to;
  191. tension(low[v], low[u]);
  192. if (low[u] == dfn[v]) {
  193. int newBcc = cntBcc ++;
  194. while (true) {
  195. int t = contain.top();
  196. if (bccFa[t]) {
  197. newGraph::insert(newBcc, bccFa[t]);
  198. }
  199. bcc[newBcc].insert(t);
  200. if (bccFa[t] == -1) idxNew[t] = newBcc;
  201. contain.pop();
  202. if (t == u) break;
  203. }
  204. if (bccFa[v] == -1) {
  205. idxNew[v] = cntBcc;
  206. bccFa[v] = cntBcc ++;
  207. newGraph::cutVertex[bccFa[v]] = v;
  208. }
  209. newGraph::cutVertex[newBcc] = v;
  210. newGraph::insert(bccFa[v], newBcc);
  211. }
  212. }
  213. for (; pt; pt = pt->next) {
  214. int u = pt->to;
  215. if (dfn[u] != -1) tension(low[v], dfn[u]);
  216. else {
  217. stk.push(make_pair(u, (Edge*) NULL));
  218. break;
  219. }
  220. }
  221. if (! pt) stk.pop();
  222. }
  223. newGraph::root = bccFa[1];
  224. idxNew[1] = bccFa[1];
  225. }
  226. }
  227. int main() {
  228. scanf("%d%d%d\n", &n, &m, &Q);
  229. for (int i = 1; i <= n; i ++) {
  230. scanf("%d\n", A + i);
  231. }
  232. srcGraph::init();
  233. srcGraph::make();
  234. newGraph::cutLightHeavy();
  235. for (int i = 0; i < Q; i ++) {
  236. char opt;
  237. int a, b;
  238. scanf("%c%d%d\n", &opt, &a, &b);
  239. if (opt == 'A') {
  240. int ans = a == b ? A[a] : newGraph::query(a, b);
  241. printf("%d\n", ans);
  242. }
  243. else {
  244. int t = idxNew[a];
  245. if (t == newGraph::root) {
  246. A[a] = b;
  247. continue;
  248. }
  249. if (srcGraph::bccFa[a] != -1) {
  250. t = newGraph::fa[t];
  251. }
  252. bcc[t].change(A[a], b);
  253. A[a] = b;
  254. zkwSegmentTree::modify(newGraph::idxMap[t], bcc[t].maxval);
  255. }
  256. }
  257. return 0;
  258. }

codeforces 487E Tourists的更多相关文章

  1. Codeforces 487E Tourists [广义圆方树,树链剖分,线段树]

    洛谷 Codeforces 思路 首先要莫名其妙地想到圆方树. 建起圆方树后,令方点的权值是双联通分量中的最小值,那么\((u,v)\)的答案就是路径\((u,v)\)上的最小值. 然而这题还有修改, ...

  2. UOJ#30/Codeforces 487E Tourists 点双连通分量,Tarjan,圆方树,树链剖分,线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html 题目传送门 - UOJ#30 题意 uoj写的很简洁.清晰,这里就不抄一遍了. 题解 首先建 ...

  3. CodeForces 487E Tourists(圆方树+线段树+树链剖分)

    题意 ​ \(n\) 个点 \(m\) 条边的无向连通图,每个点有点权,\(q\) 个要求,每次更新一个点的点权或查询两点间路径权值最小的点最小的路径. 思路 ​ 算是圆方树的板子吧?圆方树处理的主要 ...

  4. Tourists Codeforces - 487E

    https://codeforces.com/contest/487/problem/E http://uoj.ac/problem/30 显然割点走过去就走不回来了...可以看出题目跟点双有关 有一 ...

  5. CF数据结构练习

    1. CF 438D The Child and Sequence 大意: n元素序列, m个操作: 1,询问区间和. 2,区间对m取模. 3,单点修改 维护最大值, 取模时暴力对所有>m的数取 ...

  6. [UOJ30/Codeforces Round #278 E]Tourists

    传送门 好毒瘤的一道题QAQ,搞了好几好几天. UOJ上卡在了53个点,CF上过了,懒得优化常数了 刚看时一眼Tarjan搞个强连通分量然后缩点树链剖分xjb搞搞就行了,然后写完了,然后WA了QAQ. ...

  7. Solution -「CF 487E」Tourists

    \(\mathcal{Description}\)   Link.   维护一个 \(n\) 个点 \(m\) 条边的简单无向连通图,点有点权.\(q\) 次操作: 修改单点点权. 询问两点所有可能路 ...

  8. Codeforces Round #278 (Div. 2)

    题目链接:http://codeforces.com/contest/488 A. Giga Tower Giga Tower is the tallest and deepest building ...

  9. Educational Codeforces Round 21 Problem E(Codeforces 808E) - 动态规划 - 贪心

    After several latest reforms many tourists are planning to visit Berland, and Berland people underst ...

随机推荐

  1. 【原】spring boot在整合项目依赖的问题

    最近要开发新的项目,就花了几天时间看了下spring boot的相关资料,然后做了一个demo,不得不说开发效率确实很快,几行注解就完成了事务,aop,数据库等相关配置:但由于先前习惯了spring ...

  2. s3c2440栈分配情况(fl2440裸机 stack)

    //2440INIT.S ;The location of stacks UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~ SVCStac ...

  3. Linux下安装JRE

    (1)下载jre-7u5-linux-i586.tar.gz,上传至/root目录 (2)执行tar -zxf jre-7u5-linux-i586.tar.gz (3)mv jre1.7.0_05 ...

  4. Java学习02

    Java学习02 1.导入内部的包 一.在包的下面加入下面一句话: import    java.util.Scanner; 二.在类中 Scanner input=new     Sanner(Sy ...

  5. [Swust OJ 767]--将军回家(Dijkstra算法)

    题目链接:http://acm.swust.edu.cn/problem/767/ Time limit(ms): 1000 Memory limit(kb): 65535   Description ...

  6. BZOJ 1617: [Usaco2008 Mar]River Crossing渡河问题( dp )

    dp[ i ] = max( dp[ j ] + sum( M_1 ~ M_( i - j ) ) + M , sum( M_1 ~ M_i ) ) ( 1 <= j < i )  表示运 ...

  7. 进度记录 和 安装imagick时Cannot locate header file MagickWand.h错误的解决

    修改php.ini文件,已使php支持扩展的功能 [root@localhost imagick-2.2.2]# ./configure --with-php-config=/usr/local/ph ...

  8. python成长之路13

    一:SqlAlchemy ORM ORM:Object Relational Mapping 对象关系映射是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 SQLAlchemy是 ...

  9. Sort list by merge sort

    使用归并排序对链表进行排序 O(nlgn) 的时间效率 /** * Definition for singly-linked list. * struct ListNode { * int val; ...

  10. 关于QuartusII中的文件加密

    有时候我们要把工程交接给别人,但是又不希望对方看到里面的东西.在网上查找了几位大牛的博客进行整合 来自coyoo博客 http://bbs.ednchina.com/BLOG_ARTICLE_2482 ...