圆方树(广义圆方树)
圆方树(广义圆方树)
圆方树是个好东西
顾名思义,就是一颗只有圆点(原点)和方点的树
建立它的方法就是将点双中的点全部连向一个方点,然后剪完图后把原边给删掉
然后就会有一些非常奇妙的性质:
- 每条边连接一个圆点和方点
- 建出来的图是棵树
然后还有一个点双很奇gay的性质
点双上任意两点的简单路径并等于点双的全集
设方点维护与之相邻的圆点权值,那么方点圆方树上
圆方树上任意两点的简单路径并等于路径上点双的全集,也就是路径上方点的贡献
然后它就非常适合解决简单路径的min,max之类的问题,直接树剖+线段树+lca
但是如果加入修改怎么办呢?暴力修改圆点相邻的方点可能是O(N^2)
然后就有个合理的设想,每个方点只维护它的儿子圆点,于是就只用修改圆点的父亲方点了
查询时就注意lca是方点要加上它父亲圆点的贡献
然后下面这道例题还要用multiset维护方点相邻圆点的权值,以保证方点的最小
5909. 【NOIP2018模拟10.16】跑商
#include<bits/stdc++.h>
#define Fu(i,a,b) for(int i=a;i<=b;i++)
#define Fd(i,a,b) for(int i=a;i>=b;i--)
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;
int n,m,q,w[100005],dfn[200005],low[100005],d[100005],tp,tot,cn,xxx;
int top[200005],fa[200005],siz[200005],dson[200005],dep[200005];
int mix[200005<<2];
multiset<int> s[100005];
struct edge{
int fi[200005],ne[400005],to[400005],cnt;
void add(int u,int v){
ne[++cnt]=fi[u],to[cnt]=v,fi[u]=cnt;
}
}e,t;
void dfs(int o){
dfn[o]=low[o]=++tot;
d[++tp]=o;
for(int i=e.fi[o];i;i=e.ne[i]){
int v=e.to[i];
if(!dfn[v]){
dfs(v);
low[o]=min(low[o],low[v]);
if(low[v]==dfn[o]){
cn++;
int x=0;
while(x!=v){
x=d[tp--];
t.add(x,cn),t.add(cn,x);
xxx++;
}
xxx++;
t.add(o,cn),t.add(cn,o);
}
}
else low[o]=min(low[o],dfn[v]);
}
}
void dfs1(int o,int from){
fa[o]=from,siz[o]=1,dep[o]=dep[from]+1;
int mx=0;
for(int i=t.fi[o];i;i=t.ne[i]){
int v=t.to[i];
if(v==from) continue;
dfs1(v,o);
if(o>n) s[o-n].insert(w[v]);
siz[o]+=siz[v];
if(siz[v]>mx) mx=siz[v],dson[o]=v;
}
}
void dfs2(int o,int from){
dfn[o]=++tot;
if(dson[o]) top[dson[o]]=top[o],dfs2(dson[o],o);
for(int i=t.fi[o];i;i=t.ne[i]){
int v=t.to[i];
if(v==from||v==dson[o]||(o<=n)==(v<=n)) continue;
top[v]=v,dfs2(v,o);
}
}
void change(int o,int l,int r,int x,int y){
if(l==r){
mix[o]=y;
return ;
}
int mid=l+r>>1;
if(mid>=x) change(o*2,l,mid,x,y);
else change(o*2+1,mid+1,r,x,y);
mix[o]=min(mix[o*2],mix[o*2+1]);
}
int ask(int o,int l,int r,int x,int y){
if(l>=x&&r<=y) return mix[o];
int mid=l+r>>1,ans=0x7fffffff;
if(mid>=x) ans=min(ans,ask(o*2,l,mid,x,y));
if(mid<y) ans=min(ans,ask(o*2+1,mid+1,r,x,y));
return ans;
}
int lca(int x,int y){
int ans=0x7fffffff;
while(top[x]!=top[y]){
if(dep[top[x]]>dep[top[y]]){
ans=min(ans,ask(1,1,cn,dfn[top[x]],dfn[x]));
x=fa[top[x]];
}
else{
ans=min(ans,ask(1,1,cn,dfn[top[y]],dfn[y]));
y=fa[top[y]];
}
}
if(dep[x]>dep[y]) swap(x,y);
ans=min(ans,ask(1,1,cn,dfn[x],dfn[y]));
if(x>n) ans=min(ans,w[fa[x]]);
else ans=min(ans,w[x]);
return ans;
}
int main(){
fre(paoshang);
memset(mix,64,sizeof(mix));
scanf("%d%d",&n,&m),cn=n;
Fu(i,1,n) scanf("%d",&w[i]);
Fu(i,1,m){
int u,v;
scanf("%d%d",&u,&v);
e.add(u,v),e.add(v,u);
}
dfs(1);
dfs1(1,0);
top[1]=1,tot=0,dfs2(1,0);
Fu(i,n+1,cn) change(1,1,cn,dfn[i],*s[i-n].begin());
scanf("%d",&q);
Fu(i,1,q){
char f=getchar();
while(f!='C'&&f!='Q') f=getchar();
int u,v;
scanf("%d%d",&u,&v);
if(f=='C'){
if(u==1){
w[u]=v;
continue;
}
s[fa[u]-n].erase(s[fa[u]-n].find(w[u]));
s[fa[u]-n].insert(v);
w[u]=v;
change(1,1,cn,dfn[fa[u]],*s[fa[u]-n].begin());
}
else{
printf("%d\n",w[u]-lca(u,v));
}
}
return 0;
}