【题解】Uoj#30 Tourist(广义圆方树+树上全家桶)

名字听起来很霸气其实算法很简单....

仙人掌上的普通圆方树是普及题,但是广义圆方树虽然很直观但是有很多地方值得深思

说一下算法的流程:

  • 对于所有点强连通分量(强联通,意味着要找极大的那个),建立一个虚点\(u\),然后把环内所有边断开,紧接着让环内所有点向这个虚点连边。可以看出对于每一个大小为\(s\)的SCC,我们导出了一个点数为\(s+1\)边数为\(n\)的图且联通,所以圆方树是树。
  • 为了方便讨论,对于每个桥加个虚点。虚点维护SCC内所有点的信息

正确性是显然的,因为我如果要从环的某一点a走到另一点b,我一定会在这颗圆方树上经过虚点。虚点可以用不同的方法维护点权信息。

然而不好维护边权,所以我们的做法是直接把边看做一个点建图...

用Tarjan魔改一下就能找了

这一题就是模板题,直接建出来树剖即可。

然后树上的点权修改如果单次修改和度数有关是\(O(n)\)的,一个常见套路是在父亲处打tag,此时为了方便讨论加的规则就派上了用(少讨论很多东西)

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#define mid ((l+r)>>1)
#define lef l,mid,pos<<1
#define rgt mid+1,r,pos<<1|1
#define getchar() (__c==__ed?(__ed=__buf+fread(__c=__buf,1,1<<21,stdin),*__c++):*__c++) using namespace std; typedef long long ll; char __buf[1<<21],*__c=__buf,*__ed=__buf;
inline int qr(){
int ret=0,f=0,c=getchar();
while(!isdigit(c))f|=c==45,c=getchar();
while(isdigit(c)) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
} const int maxn=1e5+5;
vector<int> eg[maxn],et[maxn<<1];
int w[maxn<<1];
int dfn[maxn<<1],low[maxn],stk[maxn],top;
int arc[maxn<<1],fi[maxn<<1],d[maxn<<1],r[maxn<<1];
int seg[maxn<<3],siz[maxn<<1],son[maxn<<1];
int n,m,q,cnt; struct Pri{
multiset<int> s;
int Top;
Pri(){Top=1e9+1;s.clear();}
inline void insert(int x){s.insert(x);Top=*s.begin();}
inline void del(int x){s.erase(s.find(x));Top=*s.begin();}
inline int top(){return Top;}
}p[maxn]; void add(vector<int>*e,int fr,int to){
e[fr].push_back(to);
e[to].push_back(fr);
} void dfs(int now,int last){
stk[++top]=now; dfn[now]=low[now]=++*dfn;
for(auto t:eg[now]){
if(!dfn[t]){
dfs(t,now);
if(low[t]>=dfn[now]){
++cnt; int temp;
add(et,now,cnt);
do temp=stk[top--],add(et,cnt,temp),p[cnt-n].insert(w[temp]);
while(temp!=t);
}
low[now]=min(low[now],low[t]);
}else low[now]=min(low[now],dfn[t]);
}
} void build(int l,int r,int pos){
if(l==r) return seg[pos]=w[arc[l]],void();
build(lef); build(rgt);
seg[pos]=min(seg[pos<<1],seg[pos<<1|1]);
} void upd(int v,int p,int l,int r,int pos){
if(p<l||p>r) return;
if(l==r) return seg[pos]=v,void();
upd(v,p,lef); upd(v,p,rgt);
seg[pos]=min(seg[pos<<1],seg[pos<<1|1]);
} int que(int L,int R,int l,int r,int pos){
if(L>r||R<l) return 1e9;
if(L<=l&&r<=R) return seg[pos];
return min(que(L,R,lef),que(L,R,rgt));
} void dfs1(int now,int last){
r[now]=last; d[now]=d[last]+1;
siz[now]=1;
for(auto t:et[now])
if(!siz[t])
dfs1(t,now),siz[now]+=siz[t],son[now]=siz[son[now]]>siz[t]?son[now]:t;
} void dfs2(int now,int last){
fi[now]=last; dfn[now]=++*dfn; arc[*dfn]=now;
if(son[now]) dfs2(son[now],last);
for(auto t:et[now])
if(!dfn[t]) dfs2(t,t);
} int que(int u,int v){
int ret=1e9+1;
while(fi[u]^fi[v]){
if(d[fi[u]]<d[fi[v]]) swap(u,v);
ret=min(ret,que(dfn[fi[u]],dfn[u],1,cnt,1));
u=r[fi[u]];
}
if(d[u]<d[v]) swap(u,v);
ret=min(que(dfn[v],dfn[u],1,cnt,1),ret);
if(v>n) ret=min(ret,w[r[v]]);
return ret;
} int main(){
cnt=n=qr(); m=qr(); q=qr();
for(int t=1;t<=n;++t) w[t]=qr();
for(int t=1,a,b;t<=m;++t)
a=qr(),b=qr(),add(eg,a,b);
for(int t=1;t<=n;++t) if(!dfn[t]) dfs(t,0);
memset(dfn,0,sizeof dfn);
dfs1(1,0); dfs2(1,1);
for(int t=n+1;t<=cnt;++t) w[t]=p[t-n].top();
build(1,cnt,1);
while(q--){
char c=getchar();
while(!isalpha(c)) c=getchar();
int u=qr(),v=qr();
if(c=='A') printf("%d\n",que(u,v));
else{
upd(v,dfn[u],1,cnt,1);
if(r[u]){
p[r[u]-n].del(w[u]);
p[r[u]-n].insert(v);
upd(p[r[u]-n].top(),dfn[r[u]],1,cnt,1);
}
w[u]=v;
}
}
return 0;
}

【题解】Uoj#30 Tourist(广义圆方树+树上全家桶)的更多相关文章

  1. 圆方树&广义圆方树[学习笔记]

    仙人掌 圆方树是用来解决仙人掌图的问题的,那什么是仙人掌图呢? 如图,不存在边同时属于多个环的无向连通图是一棵仙人掌 圆方树 定义 原先的仙人掌图,通过一些奇妙的方法,可以转化为一棵由圆点,方点和树边 ...

  2. 洛谷P4606 [SDOI2018]战略游戏 [广义圆方树]

    传送门 思路 先考虑两点如何使他们不连通. 显然路径上所有的割点都满足条件. 多个点呢?也是这样的. 于是可以想到圆方树.一个点集的答案就是它的虚树里圆点个数减去点集大小. 可以把点按dfs序排序,然 ...

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

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

  4. 洛谷P4630 [APIO2018]铁人两项 [广义圆方树]

    传送门 又学会了一个新东西好开心呢~ 思路 显然,假如枚举了起始点\(x\)和终止点\(y\),中转点就必须在它们之间的简单路径上. 不知为何想到了圆方树,可以发现,如果把方点的权值记为双联通分量的大 ...

  5. BZOJ3331 压力 (圆方树+树上差分)

    题意 略 题解 求路径上的割点. 然后就直接圆方树上差分 CODE #include <bits/stdc++.h> using namespace std; inline void rd ...

  6. BZOJ3331 [BeiJing2013]压力[圆方树+树上差分]

    圆方树新技能get.具体笔记见图连通性问题学习笔记. 这题求无向图的必经点,这个是一个固定套路:首先,一张连通的无向图中,每对点双和点双之间是以一个且仅一个割点连接起来的(如果超过一个就不能是割点了) ...

  7. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  8. 仙人掌 && 圆方树 && 虚树 总结

    仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...

  9. 【NOI2013模拟】坑带的树(仙人球的同构+圆方树乱搞+计数+HASH)

    [NOI2013模拟]坑带的树 题意: 求\(n\)个点,\(m\)条边的同构仙人球个数. \(n\le 1000\) 这是一道怎么看怎么不可做的题. 这种题,肯定是圆方树啦~ 好,那么首先转为广义圆 ...

随机推荐

  1. Python从文件中读取内容,包含中文和英文

    读取文件内容使要和保存文件时的格式一致 以UTF-8格式保存文件,如: 读取: 在.py起始行写入:#-*- coding:utf-8 -*- filename = raw_input(u" ...

  2. 从开源小白到 Apache Member,我的成长之路

    我们走过的每一步路,都会留下印记,越坚实,越清晰. 近日,Apache 软件基金会(ASF)官方 Blog 宣布全球新增 40 位 Apache Member,张乎兴有幸成为其中一位. 目前,全球共有 ...

  3. Best Open Source Software

    Best Open Source Software Open Source, Software, Top The promise of open source software is best qua ...

  4. html实体字符转换成字符串

    function EntityToString(value) { let tag = document.createElement("div"); tag.innerHTML = ...

  5. STS Eclipse IDEA 指定启动JDK版本

    使用场景: 开发人员在自己的机器上可能装了多个版本的JDK,但是在环境变量中只能配置一个 JAVA_HOME ,so你的IDEA Eclipse 可能因为你在 JAVA_HOME 配置JDK1.8 以 ...

  6. oracle函数 round(x[,y])

    [功能]返回四舍五入后的值 [参数]x,y,数字型表达式,如果y不为整数则截取y整数部分,如果y>0则四舍五入为y位小数,如果y小于0则四舍五入到小数点向左第y位. [返回]数字 [示例] se ...

  7. 前端知识体系(二)http请求

    https://blog.csdn.net/Lammonpeter/article/details/81358387 一.DNS解析 首先DNS域名系统的作用是将输入的url域名解析成ip地址以方便对 ...

  8. html--多媒体文件

    添加多媒体文件 1.添加多媒体文件标记 <embed src="" width="" height=""></embed& ...

  9. python-字符编码数据类型转换

    1 - 编码格式转换 1.1 编码格式介绍 字符集 介绍 ASCII ASCII 码使用指定的7 位或8 位二进制数组合来表示128 或256 种可能的字符 ANSI ANSI是一种字符代码,为使计算 ...

  10. SpringBoot2.0--- 多数据源配置

      在开发的过程中我们可能都会遇到对接公司其他系统等需求,对于外部的系统可以采用接口对接的方式,对于一个公司开发的两个系统,并且知道相关数据库结构的情况下,就可以考虑使用多数据源来解决这个问题.Spr ...