https://codeforces.com/contest/487/problem/E

http://uoj.ac/problem/30

显然割点走过去就走不回来了...可以看出题目跟点双有关

有一个结论:如果在点双中有不同的点a,b,c,那么一定存在点不重复的路径从a到c再到b。

证明(摘自https://codeforces.com/blog/entry/14832):

显然这样的点双中,点数>=3,因此移除任意点或任意边,仍然连通

新建一张网络流图。用边(u,v,w)表示从u到v容量为w的边

对于原点双中每一条边(u,v),建新边(u,v,1),(v,u,1)

新建点S,T,建边(S,c,2),(a,T,1),(b,T,1)

用拆点的方法,给S,T,c外的每一个点1的容量(拆点过程略)

此时,显然如果S到T的最大流为2,那么存在合法方案

最大流=最小割。显然最小割<=2(割掉(a,T)与(b,T)的边)。割掉新图中任意一条权值为1的边(除(a,T),(b,T)),对应删除原点双的一条边或一个点,显然删除之后原图仍然连通,则新图也连通。因此最小割>1。所以最小割=2

就是说,如果走的时候要穿过一个点双(任意点开始,任意点结束),那么一定可以找到一条合法路径经过点双内权值最小的点

有一个想法:

可以点双缩点成树(圆方树)(每个点双新建一个点,向点双中所有点连边,删除原图中所有边)

(树中代表一个点双的点的权值是点双中点权的最小值)

然后查询就是树上两点路径上最小值,修改就是单点修改(每个点双用一个multiset维护点双中所有点的权值。修改非割点就直接在它属于的点双的multiset中改,如果multiset中最小值变了就对应修改维护最小值的数据结构;修改割点方法在后面),用树剖维护即可

好像还不怎么对。。割点也是属于点双的,但是修改割点权值显然不能暴力修改其属于的所有点双

看了题解,方法是:每个割点就当做属于它在缩点树中的父亲(这个父亲一定代表一个点双;如果不存在父亲就不用维护),每次修改就在这个父亲的multiset那里改;同时每次查询的时候,如果两点的lca代表一个点双,那么除两点路径上最小值外还要考虑lca的父亲(一定是个割点;如果不存在父亲就不用额外查)

然而真的码不动。。5K代码,续了一下午A掉

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<vector>
  5. #include<set>
  6. using namespace std;
  7. #define fi first
  8. #define se second
  9. #define mp make_pair
  10. #define pb push_back
  11. typedef long long ll;
  12. typedef unsigned long long ull;
  13. typedef pair<int,int> pii;
  14. struct E
  15. {
  16. int to,nxt;
  17. };
  18. int a[];
  19. int n,m,qq;
  20. namespace S
  21. {
  22. #define lc (num<<1)
  23. #define rc (num<<1|1)
  24. int d[];
  25. void setx(int L,int x,int l,int r,int num)
  26. {
  27. if(l==r) {d[num]=x;return;}
  28. int mid=l+((r-l)>>);
  29. if(L<=mid) setx(L,x,l,mid,lc);
  30. else setx(L,x,mid+,r,rc);
  31. d[num]=min(d[lc],d[rc]);
  32. }
  33. int gmin(int L,int R,int l,int r,int num)
  34. {
  35. if(L<=l&&r<=R) return d[num];
  36. int mid=l+((r-l)>>);int ans=0x3f3f3f3f;
  37. if(L<=mid) ans=min(ans,gmin(L,R,l,mid,lc));
  38. if(mid<R) ans=min(ans,gmin(L,R,mid+,r,rc));
  39. return ans;
  40. }
  41. int getmin(int L,int R,int l,int r,int num)
  42. {
  43. if(L>R) swap(L,R);
  44. //printf("19t%d %d %d\n",L,R,gmin(L,R,l,r,num));
  45. return gmin(L,R,l,r,num);
  46. }
  47. }
  48. int cnt;
  49. int bno[];
  50. bool iscut[];
  51. namespace T
  52. {
  53. E e[];
  54. int f1[],ne;
  55. void me(int x,int y)
  56. {
  57. e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;
  58. e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;
  59. }
  60. bool vis[];
  61. int sz[],hson[],tp[],dep[];
  62. void dfs1(int u,int fa)
  63. {
  64. sz[u]=;
  65. vis[u]=;
  66. for(int k=f1[u];k;k=e[k].nxt)
  67. if(e[k].to!=fa)
  68. {
  69. dep[e[k].to]=dep[u]+;
  70. dfs1(e[k].to,u);
  71. sz[u]+=sz[e[k].to];
  72. if(sz[e[k].to]>sz[hson[u]])
  73. hson[u]=e[k].to;
  74. }
  75. }
  76. int ar[],lp[],f[];
  77. void dfs2(int u,int fa)
  78. {
  79. ar[++ar[]]=u;lp[u]=ar[];
  80. f[u]=fa;
  81. if(u==hson[fa]) tp[u]=tp[fa];
  82. else tp[u]=u;
  83. if(hson[u]) dfs2(hson[u],u);
  84. for(int k=f1[u];k;k=e[k].nxt)
  85. if(e[k].to!=fa&&e[k].to!=hson[u])
  86. dfs2(e[k].to,u);
  87. }
  88. multiset<int> s[];//s[i]维护i号点双里面所有非割点的权值
  89. void update2(int x)
  90. {
  91. a[x+n]=*s[x].begin();
  92. //printf("9t%d %d\n",x,a[x+n]);
  93. S::setx(lp[x+n],a[x+n],,n+cnt,);
  94. }
  95. void change(int x,int y)
  96. {
  97. if(!iscut[x])
  98. {
  99. s[bno[x]].erase(s[bno[x]].find(a[x]));
  100. s[bno[x]].insert(a[x]=y);
  101. update2(bno[x]);
  102. }
  103. else
  104. {
  105. if(f[x])
  106. {
  107. s[f[x]-n].erase(s[f[x]-n].find(a[x]));
  108. s[f[x]-n].insert(y);
  109. update2(f[x]-n);
  110. }
  111. a[x]=y;
  112. S::setx(lp[x],y,,n+cnt,);
  113. }
  114. }
  115. int query(int x,int y)
  116. {
  117. if(x==y) return a[x];
  118. //printf("99t%d %d\n",x,y);
  119. //if(!iscut[x]) x=n+bno[x];
  120. //if(!iscut[y]) y=n+bno[y];
  121. int ans=0x3f3f3f3f;
  122. while(tp[x]!=tp[y])
  123. {
  124. if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
  125. //printf("7t%d %d\n",x,y);
  126. ans=min(ans,S::getmin(lp[x],lp[tp[x]],,n+cnt,));
  127. x=f[tp[x]];
  128. }
  129. ans=min(ans,S::getmin(lp[x],lp[y],,n+cnt,));
  130. if(dep[x]>dep[y]) swap(x,y);
  131. if(x>n&&f[x]) ans=min(ans,a[f[x]]);
  132. return ans;
  133. }
  134. void work()
  135. {
  136. int i;
  137. /*
  138. for(i=1;i<=n+cnt;i++)
  139. for(int k=f1[i];k;k=e[k].nxt)
  140. printf("1t%d %d\n",i,e[k].to);
  141. for(i=1;i<=n;i++)
  142. printf("%d ",bno[i]);
  143. puts("t2");
  144. for(i=1;i<=n;i++)
  145. printf("%d ",int(iscut[i]));
  146. puts("t3");
  147. */
  148. for(i=;i<=n+cnt;i++)
  149. if(!vis[i])
  150. {
  151. dfs1(i,);
  152. dfs2(i,);
  153. }
  154. /*
  155. for(i=1;i<=ar[0];i++)
  156. printf("%d ",ar[i]);
  157. puts("t4");
  158. for(i=1;i<=n+cnt;i++)
  159. printf("%d ",lp[i]);
  160. puts("t5");
  161. for(i=1;i<=n+cnt;i++)
  162. printf("6t%d %d\n",f[i],tp[i]);
  163. */
  164. for(i=;i<=n;i++)
  165. if(!iscut[i])
  166. {
  167. s[bno[i]].insert(a[i]);
  168. S::setx(lp[i],0x3f3f3f3f,,n+cnt,);
  169. }
  170. else
  171. {
  172. if(f[i])
  173. {
  174. s[f[i]-n].insert(a[i]);
  175. }
  176. S::setx(lp[i],a[i],,n+cnt,);
  177. }
  178. for(i=;i<=cnt;i++)
  179. update2(i);
  180. //for(i=1;i<=ar[0];i++)
  181. // printf("16t%d\n",S::getmin(i,i,1,n+cnt,1));
  182. }
  183.  
  184. }
  185. namespace G
  186. {
  187. E e[];
  188. int f1[],ne=;
  189. void me(int x,int y)
  190. {
  191. e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;
  192. e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;
  193. }
  194. int dfc,dfn[];
  195. pii st[];int tp;
  196. int dfs(int u,int last)
  197. {
  198. //printf("3t%d %d\n",u,last);
  199. int k,v,lowu=dfn[u]=++dfc,chi=,lowv;pii x;
  200. for(k=f1[u];k;k=e[k].nxt)
  201. {
  202. v=e[k].to;
  203. if(!dfn[v])
  204. {
  205. st[++tp]=mp(u,v);++chi;
  206. lowv=dfs(v,k);lowu=min(lowu,lowv);
  207. if(lowv>=dfn[u])
  208. {
  209. //printf("4t%d %d\n",u,v);
  210. iscut[u]=;
  211. ++cnt;
  212. for(;;)
  213. {
  214. x=st[tp--];
  215. if(bno[x.fi]!=cnt)
  216. {
  217. bno[x.fi]=cnt;
  218. T::me(n+cnt,x.fi);
  219. }
  220. if(bno[x.se]!=cnt)
  221. {
  222. bno[x.se]=cnt;
  223. T::me(n+cnt,x.se);
  224. }
  225. if(x.fi==u&&x.se==v) break;
  226. }
  227. }
  228. }
  229. else if(dfn[v]<dfn[u]&&k!=(last^))
  230. {
  231. st[++tp]=mp(u,v);
  232. lowu=min(lowu,dfn[v]);
  233. }
  234. }
  235. if(!last&&chi==) iscut[u]=;
  236. //printf("5t%d %d %d\n",u,lowu,dfn[u]);
  237. return lowu;
  238. }
  239. }
  240.  
  241. int main()
  242. {
  243. char tmp[];
  244. int i,x,y;
  245. scanf("%d%d%d",&n,&m,&qq);
  246. for(i=;i<=n;i++) scanf("%d",&a[i]);
  247. for(i=;i<=m;i++)
  248. {
  249. scanf("%d%d",&x,&y);
  250. G::me(x,y);
  251. }
  252. for(i=;i<=n;i++)
  253. if(!G::dfn[i])
  254. G::dfs(i,);
  255. T::work();
  256. while(qq--)
  257. {
  258. scanf("%s%d%d",tmp,&x,&y);
  259. if(tmp[]=='C')
  260. {
  261. T::change(x,y);
  262. }
  263. else
  264. {
  265. printf("%d\n",T::query(x,y));
  266. }
  267. }
  268. return ;
  269. }

Tourists Codeforces - 487E的更多相关文章

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

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

  2. codeforces 487E Tourists

    如果不是uoj上有的话(听说这是China Round),我有可能就错过这道题目了(这是我有史以来为oi写的最长的代码,用了我一天TAT!). 题目 传送门. 一个连通无向图,点上有权,支持两种操作: ...

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

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

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

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

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

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

  6. Solution -「CF 487E」Tourists

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

  7. Codeforces Round #278 (Div. 2)

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

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

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

  9. Codeforces Beta Round #3 B. Lorry 暴力 二分

    B. Lorry 题目连接: http://www.codeforces.com/contest/3/problem/B Description A group of tourists is goin ...

随机推荐

  1. h5的缓存机制

    H5的缓存,大概有localstorage.sessionstorage.cookie和manifest. 一.LocalStorage LocalStorage是永久性的本地缓存,存储在客户端的浏览 ...

  2. requirejs测试

    参考资料:http://www.ruanyifeng.com/blog/2012/11/require_js.html 一.文件目录 二.html <!DOCTYPE html> < ...

  3. UVA11892 ENimEN —— 博弈

    题目链接:https://vjudge.net/problem/UVA-11892 题意: 两人玩游戏,有n堆石子,每堆有ai块石子,两人轮流取,要求一次只能选择一堆石子取任意块.最后取完的获胜. 题 ...

  4. Java 8新特性之旅:使用Stream API处理集合

    在这篇“Java 8新特性教程”系列文章中,我们会深入解释,并通过代码来展示,如何通过流来遍历集合,如何从集合和数组来创建流,以及怎么聚合流的值. 在之前的文章“遍历.过滤.处理集合及使用Lambda ...

  5. wiki中文语料+word2vec (python3.5 windows win7)

    环境: win7+python3.5 1. 下载wiki中文分词语料   使用迅雷下载会快不少,大小为1个多G      https://dumps.wikimedia.org/zhwiki/late ...

  6. 编程模式(schema) —— 表驱动法(table-driven)

    使用表驱动法,而非繁琐冗长的 if/else, switch case(本身也代表一种代码坏味道),也是替身编程质量的重要手段, 表驱动法是一种编程模式(schema)-- 从表里面查找信息而不使用逻 ...

  7. bzoj 4260 Codechef REBXOR——trie树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4260 一段的异或和就是两个前缀的异或和.正反扫两边,用trie树算每个位置为左/右端点时最大 ...

  8. android jni java类型与c语言类型互换

    1.java String转换 C str char* Jstring2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsst ...

  9. Python2.7 urlparse学习

    urlparse模块主要是把url拆分为6部分,并返回元组.并且可以把拆分后的部分再组成一个url.主要有函数有urljoin.urlsplit.urlunsplit.urlparse等. urlpa ...

  10. ubuntu--vim 技巧

    本文介绍了vi (vim)的基本使用方法,但对于普通用户来说基本上够了! vi编辑器是所有Unix及Linux系统下标准的编 辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法 ...