【题解】Uoj#30 Tourist(广义圆方树+树上全家桶)
【题解】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(广义圆方树+树上全家桶)的更多相关文章
- 圆方树&广义圆方树[学习笔记]
仙人掌 圆方树是用来解决仙人掌图的问题的,那什么是仙人掌图呢? 如图,不存在边同时属于多个环的无向连通图是一棵仙人掌 圆方树 定义 原先的仙人掌图,通过一些奇妙的方法,可以转化为一棵由圆点,方点和树边 ...
- 洛谷P4606 [SDOI2018]战略游戏 [广义圆方树]
传送门 思路 先考虑两点如何使他们不连通. 显然路径上所有的割点都满足条件. 多个点呢?也是这样的. 于是可以想到圆方树.一个点集的答案就是它的虚树里圆点个数减去点集大小. 可以把点按dfs序排序,然 ...
- Codeforces 487E Tourists [广义圆方树,树链剖分,线段树]
洛谷 Codeforces 思路 首先要莫名其妙地想到圆方树. 建起圆方树后,令方点的权值是双联通分量中的最小值,那么\((u,v)\)的答案就是路径\((u,v)\)上的最小值. 然而这题还有修改, ...
- 洛谷P4630 [APIO2018]铁人两项 [广义圆方树]
传送门 又学会了一个新东西好开心呢~ 思路 显然,假如枚举了起始点\(x\)和终止点\(y\),中转点就必须在它们之间的简单路径上. 不知为何想到了圆方树,可以发现,如果把方点的权值记为双联通分量的大 ...
- BZOJ3331 压力 (圆方树+树上差分)
题意 略 题解 求路径上的割点. 然后就直接圆方树上差分 CODE #include <bits/stdc++.h> using namespace std; inline void rd ...
- BZOJ3331 [BeiJing2013]压力[圆方树+树上差分]
圆方树新技能get.具体笔记见图连通性问题学习笔记. 这题求无向图的必经点,这个是一个固定套路:首先,一张连通的无向图中,每对点双和点双之间是以一个且仅一个割点连接起来的(如果超过一个就不能是割点了) ...
- 仙人掌&圆方树学习笔记
仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...
- 仙人掌 && 圆方树 && 虚树 总结
仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...
- 【NOI2013模拟】坑带的树(仙人球的同构+圆方树乱搞+计数+HASH)
[NOI2013模拟]坑带的树 题意: 求\(n\)个点,\(m\)条边的同构仙人球个数. \(n\le 1000\) 这是一道怎么看怎么不可做的题. 这种题,肯定是圆方树啦~ 好,那么首先转为广义圆 ...
随机推荐
- QT 中如何实现一个简单的动画
QT可以实现一下简单的动画,比如 运动的时钟 闪烁的按钮. 动画的实现: (1)创建一个定时器 (2)调用QWidget::update()通知界面重绘 实现一个按钮闪烁的例子: circlewidg ...
- asp.net如何判断网址能否打开
private void StatusDescription_Click(object sender, System.EventArgs e) { PageUrl = ...
- React Native声明属性和属性确认
属性声明 因为用React Native创建的自定义组件可以复用, 我们开发过程中可能一个项目组有多个人同时开发,其他同事可能会用到我们自定义的组件, 但是他们使用的时候很容易忘记使用某些属性,这时候 ...
- epoll简介(一)
一:概述 1:简介 EPOLL类似于POLL,是Linux特有的一种IO多路复用的机制.它在2.5.44内核中引入. 对于大量的描述符处理,EPOLL更有优势,它提供了三个系统调用来创建管理epo ...
- 「POI2012」约会 Rendezvous
#2691. 「POI2012」约会 Rendezvous 这题我简直不想说什么了,什么素质,卡常数…… “每个顶点有且仅有一条出边”,所以是一道基环树的题,首先tarjan缩点,在缩完点后的图上求a ...
- 当flex遇到white-space: nowrap; 排版就飞了的神奇问题 吐血
在做一个移动端排班的时候需要某一行的字超出的部分用省略号代替 然后写着发现后面排版乱了 HTML结构如下 我想让第二个span的内容加省略号 css正确的代码如下 .list { width: 1 ...
- swiper仿tab栏切换
转载 https://developers.weixin.qq.com/community/develop/article/doc/000040a5dc4518005d2842fdf51c13 小程 ...
- Vue 路由的嵌套使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- java反斜杠替换
java replaceAll() 方法要用 4 个反斜杠,表示一个反斜杠 例如 str1="aa\bbb"; str2="aa'bbb"; 要想替换成 str ...
- QQ三方登录
申请了十天,最终通过了审核,能够上线了,事实上申请的第一天,站点的qq登录已经做好了,而且能够用測试帐号登录,但提交审核后,总是通只是,提示:您的站点审核未通过.原因是"未放置QQ登录but ...