uoj#30 Tourist (圆方树学习笔记)
WC陈俊锟课件
学会了可删除堆
例题1 : 求图中两点间路径上最大(最小)权值
无修改
设圆点权值为本身,方点权值为点双中的最大(小)权值
查询链上最大值
并查集??
有修改
设圆点权值为本身,方点权值为点双中非根节点的最大(小)权值
修改时修改父方点的权值(堆维护)
注意: 查询时如果LCA是方点,还要考虑方点的父圆点的权值
uoj#30
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<stack> #include<cmath> using namespace std; typedef long long ll; const int maxn = 200010; const int INF = 2e9+7; int n,m,qq; int w[maxn]; char op[10]; int h[maxn],size,h1[maxn],size1; struct E{ int to,next; }e[maxn<<1],e1[maxn<<1]; void add(int u,int v){ e[++size].to=v; e[size].next=h[u]; h[u]=size; } void add1(int u,int v){ e1[++size1].to=v; e1[size1].next=h1[u]; h1[u]=size1; } struct Set{ // 可删除堆 priority_queue<int,vector<int>,greater<int> > q1,q2; void insert(int x) { q1.push(x); } void erase(int x){ q2.push(x); } int top(){ while((!q2.empty())&&(q1.top()==q2.top())){ q1.pop(); q2.pop(); } return q1.top(); } }q[maxn]; struct Node{ int mn; }t[maxn<<2]; int fa[maxn],sz[maxn],son[maxn],top[maxn],dep[maxn],id[maxn]; int dfn[maxn],low[maxn],tim,tot,sta[maxn],topp; void tarjan(int u){ dfn[u]=low[u]=++tim; sta[++topp]=u; for(int i=h[u];i!=-1;i=e[i].next){ int v=e[i].to; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ ++tot; add1(u,tot),add1(tot,u); int x=0; do{ x=sta[topp--]; add1(x,tot),add1(tot,x); }while(x!=v); } } else low[u]=min(low[u],dfn[v]); } } void dfs1(int u,int par){ sz[u]=1; fa[u]=par; dep[u]=dep[par]+1; if(u<=n&&par) q[par].insert(w[u]); // 更新方点权值 int maxson=-1; for(int i=h1[u];i!=-1;i=e1[i].next){ int v=e1[i].to; if(v==par) continue; dfs1(v,u); sz[u]+=sz[v]; if(maxson<sz[v]){ maxson=sz[v]; son[u]=v; } } } void dfs2(int u,int par){ dfn[u]=++tim; id[tim]=u; top[u]=par; if(!son[u]) return; dfs2(son[u],par); for(int i=h1[u];i!=-1;i=e1[i].next){ int v=e1[i].to; if(v==fa[u]||v==son[u]) continue; dfs2(v,v); } } void pushup(int i){ t[i].mn=min(t[i<<1].mn,t[i<<1|1].mn); } void update(int i,int k,int l,int r,int p){ if(l==r){ t[i].mn=k; return; } int mid=(l+r)/2; if(p<=mid) update(i<<1,k,l,mid,p); else update(i<<1|1,k,mid+1,r,p); pushup(i); } int query(int i,int l,int r,int x,int y){ if(x<=l&&r<=y){ return t[i].mn; } int mid=(l+r)/2; if(y<=mid) return query(i<<1,l,mid,x,y); if(x>mid) return query(i<<1|1,mid+1,r,x,y); return min(query(i<<1,l,mid,x,y),query(i<<1|1,mid+1,r,x,y)); } int qry(int u,int v){ int res=INF; while(top[u]^top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); res=min(res,query(1,1,tot,dfn[top[u]],dfn[u])); u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); res=min(res,query(1,1,tot,dfn[u],dfn[v])); if(u>n) res=min(res,w[fa[u]]); // 如果LCA是方点,还要考虑其父节点的贡献 return res; } ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;} int main(){ memset(h,-1,sizeof(h)); memset(h1,-1,sizeof(h1)); n=read(),m=read(),qq=read(); for(int i=1;i<=n;i++) w[i]=read(); int u,v; for(int i=1;i<=m;i++){ u=read(),v=read(); add(u,v),add(v,u); } tot=n; for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i); } tim=0; dfs1(1,0); dfs2(1,1); for(int i=1;i<=n;i++) update(1,w[i],1,tot,dfn[i]); for(int i=n+1;i<=tot;i++) update(1,q[i].top(),1,tot,dfn[i]); for(int i=1;i<=qq;i++){ scanf("%s",op); u=read(),v=read(); if(op[0]=='C'){ // 每个方点维护的信息是点双内非根节点的最小值 if(fa[u]) q[fa[u]].erase(w[u]); w[u]=v; update(1,w[u],1,tot,dfn[u]); if(fa[u]){ q[fa[u]].insert(w[u]); update(1,q[fa[u]].top(),1,tot,dfn[fa[u]]); } }else{ printf("%d\n",qry(u,v)); } } return 0; }
例题2: