做这题的时候有点怂。。基本已经想到正解了。。结果感觉做法有点假,还是看了正解题解。。

首先提到简单路径上经过的点,就想到了一个关于点双的结论:两点间简单路径上所有可能经过的点的并等于路径上所有点所在点双的并,也就是说,在建一棵圆方树,方点表示所在点双里的最小点权,两个圆点之间的路径上所有方点的最小值就是答案。

然后这题有一个修改单点。。修改一个圆点的点权的时候和他相邻的方点维护的min都可能变,所以每个方点开一个multiset维护点双最小值就行了。

但是这样复杂度不能保证,因为每次圆点可能会和一堆方点相连,菊花图的时候就是单次$O(n\log n)$了。。

所以要考虑减少没用的修改。考虑到圆方树是一棵树(废话),对于一个圆点,修改的话,会影响到他的父亲方点和儿子方点。

但是,儿子方点影响不大,如果所有儿子方点的multiset里都不维护这个圆点,每次要查询儿子方点的min只需要和这个圆点合并取min就行了。

所以,每次只要把自己修改一下,把父亲方点对应的multiset修改一下,在树剖的线段树上传一下就行了。

所以,multiset只维护所有儿子圆点。可以结合下图。

如果修改紫色点,那么,对于3路径,lca是一个儿子方点,将其和紫色圆点合并min。。对于2路径,直接跨过紫色圆点了就不管了,对于1号路径,因为所在点双min可能会改掉,所以必须把父亲方点multiset改掉。

然后就随便打个树剖就行了。

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<set>
  6. #include<cmath>
  7. #include<queue>
  8. #define mst(x) memset(x,0,sizeof x)
  9. #define dbg(x) cerr << #x << " = " << x <<endl
  10. #define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
  11. using namespace std;
  12. typedef long long ll;
  13. typedef double db;
  14. typedef pair<int,int> pii;
  15. template<typename T>inline T _min(T A,T B){return A<B?A:B;}
  16. template<typename T>inline T _max(T A,T B){return A>B?A:B;}
  17. template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
  18. template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
  19. template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
  20. template<typename T>inline T read(T&x){
  21. x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
  22. while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
  23. }
  24. const int N=2e5+,INF=0x3f3f3f3f;
  25. struct thxorz{
  26. int head[N],nxt[N<<],to[N<<],tot;
  27. inline void add(int x,int y){
  28. to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
  29. to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
  30. }
  31. }G1,G2;
  32. char opt[];
  33. int A[N];
  34. int n,m,Q;
  35. #define y G1.to[j]
  36. int dfn[N],low[N],stk[N],top,tim2,cnt;
  37. void tarjan(int x){
  38. dfn[x]=low[x]=++tim2,stk[++top]=x;
  39. for(register int j=G1.head[x];j;j=G1.nxt[j]){
  40. if(!dfn[y]){
  41. tarjan(y),MIN(low[x],low[y]);
  42. if(low[y]==dfn[x]){
  43. int tmp;++cnt;
  44. do tmp=stk[top--],G2.add(cnt,tmp);while(tmp^y);
  45. G2.add(cnt,x);
  46. }
  47. }
  48. else MIN(low[x],dfn[y]);
  49. }
  50. }
  51. #undef y
  52. multiset<int> s[N];
  53. int fa[N],topfa[N],son[N],sum[N],dep[N],pos[N],id[N],tim;
  54. #define y G2.to[j]
  55. void dfs1(int x,int fat){//dbg(x);
  56. fa[x]=fat,dep[x]=dep[fat]+,sum[x]=;int tmp=-;
  57. for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fat)dfs1(y,x),sum[x]+=sum[y],MAX(tmp,sum[y])&&(son[x]=y);
  58. }
  59. void dfs2(int x,int topf){
  60. topfa[x]=topf,pos[x]=++tim,id[tim]=x;
  61. if(!son[x])return;
  62. dfs2(son[x],topf);
  63. if(x>n){
  64. s[x].insert(A[son[x]]);
  65. for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y),s[x].insert(A[y]);
  66. }
  67. else for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y);
  68. }
  69. #undef y
  70. struct SGT{
  71. int minv[N<<];
  72. #define lc i<<1
  73. #define rc i<<1|1
  74. inline void pushup(int i){minv[i]=_min(minv[lc],minv[rc]);}
  75. void build(int i,int L,int R){
  76. if(L==R){//dbg2(i,id[L]),dbg2(L,R);
  77. if(id[L]>n)minv[i]=*s[id[L]].begin();
  78. else minv[i]=A[id[L]];//dbg(minv[i]);
  79. return;
  80. }
  81. int mid=L+R>>;
  82. build(lc,L,mid),build(rc,mid+,R);pushup(i);
  83. }
  84. int query_min(int i,int L,int R,int ql,int qr){
  85. if(ql<=L&&qr>=R)return minv[i];
  86. int mid=L+R>>,ret=INF;
  87. if(ql<=mid)MIN(ret,query_min(lc,L,mid,ql,qr));
  88. if(qr>mid)MIN(ret,query_min(rc,mid+,R,ql,qr));
  89. return ret;
  90. }
  91. void update(int i,int L,int R,int x,int preval,int val){
  92. if(L==R){
  93. if(id[L]>n)s[id[L]].erase(s[id[L]].find(preval)),s[id[L]].insert(val),minv[i]=*s[id[L]].begin();
  94. else minv[i]=A[id[L]]=val;
  95. return;
  96. }
  97. int mid=L+R>>;
  98. if(x<=mid)update(lc,L,mid,x,preval,val);
  99. else update(rc,mid+,R,x,preval,val);
  100. pushup(i);
  101. }
  102. }T;
  103. inline int ask(int x,int y){
  104. int ret=INF;
  105. while(topfa[x]^topfa[y]){
  106. if(dep[topfa[x]]<dep[topfa[y]])_swap(x,y);
  107. MIN(ret,T.query_min(,,cnt,pos[topfa[x]],pos[x])),x=fa[topfa[x]];
  108. }
  109. if(dep[x]>dep[y])_swap(x,y);
  110. MIN(ret,T.query_min(,,cnt,pos[x],pos[y]));
  111. if(x>n)MIN(ret,A[fa[x]]);
  112. return ret;
  113. }
  114. inline void change(int x,int val){
  115. if(fa[x])T.update(,,cnt,pos[fa[x]],A[x],val);
  116. T.update(,,cnt,pos[x],,val);
  117. }
  118.  
  119. int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
  120. cnt=read(n),read(m),read(Q);
  121. for(register int i=;i<=n;++i)read(A[i]);
  122. for(register int i=,x,y;i<=m;++i)read(x),read(y),G1.add(x,y);
  123. for(register int i=;i<=n;++i)if(!dfn[i])top=,tarjan(i);
  124. dfs1(,),dfs2(,),T.build(,,cnt);
  125. for(register int i=,x,val,y;i<=Q;++i){
  126. scanf("%s",opt);
  127. if(opt[]=='A')read(x),read(y),printf("%d\n",ask(x,y));
  128. else read(x),read(val),change(x,val);
  129. }
  130. return ;
  131. }

总结:这题主要是以减少修改,增加一些合并操作为目标,同时注意要把圆方树是树的性质给用好(就是父亲和儿子的讨论什么的)。。

CF487E Tourists[圆方树+树剖(线段树套set)]的更多相关文章

  1. CF487E Tourists(圆方树+树链剖分+multiset/可删堆)

    CF487E Tourists(圆方树+树链剖分+multiset/可删堆) Luogu 给出一个带点权的无向图,两种操作: 1.修改某点点权. 2.询问x到y之间简单路径能走过的点的最小点权. 题解 ...

  2. CF487E Tourists + 圆方树学习笔记(圆方树+树剖+线段树+multiset)

    QWQ果然我已经什么都学不会的人了. 这个题目要求的是图上所有路径的点权和!QWQ(我只会树上啊!) 这个如果是好啊 这时候就需要 圆方树! 首先在介绍圆方树之前,我们先来一点简单的前置知识 首先,我 ...

  3. CF487E Tourists 圆方树、树链剖分

    传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就 ...

  4. Tourists——圆方树

    CF487E Tourists 一般图,带修求所有简单路径代价. 简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了. 所以可以圆方树,然后方点维护一下V-DCC内的最小值. 那么, ...

  5. BZOJ_2238_Mst_树剖+线段树

    BZOJ_2238_Mst_树剖+线段树 Description 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影 ...

  6. BZOJ_4551_[Tjoi2016&Heoi2016]树_树剖+线段树

    BZOJ_4551_[Tjoi2016&Heoi2016]树_树剖+线段树 Description 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为 ...

  7. BZOJ_2157_旅游_树剖+线段树

    BZOJ_2157_旅游_树剖+线段树 Description Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但 ...

  8. 【BZOJ5210】最大连通子块和 树剖线段树+动态DP

    [BZOJ5210]最大连通子块和 Description 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块 ...

  9. [LNOI2014]LCA(树剖+线段树)

    \(\%\%\% Fading\) 此题是他第一道黑题(我的第一道黑题是蒲公英) 一直不敢开,后来发现是差分一下,将询问离线,树剖+线段树维护即可 \(Code\ Below:\) #include ...

随机推荐

  1. UWP笔记-边框背景虚化效果

    这是一个简单的UI外观 1.添加Negut包: Microsoft.Toolkit.Uwp.UI.Controls 2.xaml:命名空间中引用 xmlns:controls="using: ...

  2. Hadoop环境搭建过程中遇到的问题以及解决方法

    1.启动hadoop之前,ssh免密登录slave主机正常,使用命令start-all.sh启动hadoop时,需要输入slave主机的密码,说明ssh文件权限有问题,需要执行以下操作: 1)进入.s ...

  3. 剑指offer17:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

    1 题目描述 输入两棵二叉树A,B,判断B是不是A的子结构.(ps:我们约定空树不是任意一个树的子结构) 2 思路和方法 (1)先在A中找和B的根节点相同的结点 (2)找到之后遍历对应位置的其他结点, ...

  4. 标准库中最值得关注的两个 装饰器是 lru_cache 和全新的 singledispatch(Python 3.4 新增)

    clock 装饰器 def clock(func): @functools.wraps(func) def clocked(*args, **kwargs): t0 = time.perf_count ...

  5. spring-data-redis 2.0 的使用

    在使用Spring Boot2.x运行Redis时,发现百度不到顺手的文档,搞通后发现其实这个过程非常简单和简洁,觉得有必要拿出来分享一下. Spring Boot2.x 不再使用Jedis,换成了L ...

  6. 【深度森林第三弹】周志华等提出梯度提升决策树再胜DNN

    [深度森林第三弹]周志华等提出梯度提升决策树再胜DNN   技术小能手 2018-06-04 14:39:46 浏览848 分布式 性能 神经网络   还记得周志华教授等人的“深度森林”论文吗?今天, ...

  7. 怎样初始化XMLHttpRequest实例对象xhr

    xhr.open() 接收5个参数, 用于初始化一个http请求, 它接收5个参数: 1. method: 请求类型; 2. url: 请求的url; 3. async: 是否为异步, 默认为true ...

  8. Java 并发进阶常见面试题总结

    声明:本文内容完全来自网络,转自GitHub->JavaGuide(https://github.com/Snailclimb/JavaGuide),致谢      1. synchronize ...

  9. C# 使用Emit实现动态AOP框架 (一)

    目  录 C# 使用Emit实现动态AOP框架 (一) C# 使用Emit实现动态AOP框架 (二) C# 使用Emit实现动态AOP框架 (三) C# 使用Emit实现动态AOP框架 进阶篇之异常处 ...

  10. IOC+EF+Core项目搭建IOC注入及框架(二)

    配置ServiceCollection /// <summary> /// 表示IServiceCollection的扩展 /// </summary> public stat ...