UOJ#30/Codeforces 487E Tourists 點雙連通分量,Tarjan,圓方樹,樹鏈剖分,線段樹
阿新 • • 發佈:2018-10-16
con multi blank uil back 時間 雙連通分量 rdquo 簡潔
原文鏈接https://www.cnblogs.com/zhouzhendong/p/UOJ30.html
題目傳送門 - UOJ#30
題意
uoj寫的很簡潔、清晰,這裏就不抄一遍了。
題解
首先建出圓方樹。接下來,我們稱"圓點"為原來有的點,"方點"為新增的點。
然後先只考慮在線詢問如何做。
——把方點的值設置成所有與他連邊的圓點的權值的最小值,直接在圓方樹上樹鏈剖分再套個線段樹支持一下區間詢問即可。
然後會發現這樣做支持不了修改操作。
——直接來個菊花圖不斷修改根節點就GG了。
於是我們考慮進一步想辦法。
我們把方點的值重新定義成“在圓方樹上,該點的兒子的權值的最小值”。那麽,在詢問的時候,其他都一樣,但是如果 lca 為方點,那麽加上其 fa 對 min 的貢獻即可。
時間復雜度 $O(n\log ^2 n )$ 。
代碼
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } const int N=200005,INF=1.1e9; int pr[N],dfn[N],low[N],st[N],Time,top,tot; multiset <int> Mins[N]; vector <int> G[N],g[N]; void Tarjan(int x){ low[x]=dfn[x]=++Time,st[++top]=x; for (auto y : G[x]) if (!dfn[y]){ Tarjan(y); low[x]=min(low[x],low[y]); if (low[y]>=dfn[x]){ tot++; g[x].push_back(tot); g[tot].push_back(x); int z; do { z=st[top--]; g[z].push_back(tot); g[tot].push_back(z); } while (z!=y); } } else low[x]=min(low[x],dfn[y]); } namespace sp{ int n,outn; int fa[N],son[N],size[N],depth[N],top[N],p[N],ap[N],cnp=0,val[N]; void dfs(int x,int pre,int d){ size[x]=1,fa[x]=pre,son[x]=-1,depth[x]=d; for (auto y : g[x]) if (y!=pre){ dfs(y,x,d+1); size[x]+=size[y]; if (son[x]==-1||size[y]>size[son[x]]) son[x]=y; } } void Get_Top(int x,int tp){ top[x]=tp; ap[p[x]=++cnp]=x; if (son[x]==-1) return; Get_Top(son[x],tp); for (auto y : g[x]) if (y!=fa[x]&&y!=son[x]) Get_Top(y,y); } int Min[N<<2]; void build(int rt,int L,int R){ if (L==R) return (void)(Min[rt]=val[ap[L]]); int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; build(ls,L,mid); build(rs,mid+1,R); Min[rt]=min(Min[ls],Min[rs]); } int query(int rt,int L,int R,int xL,int xR){ if (xL>xR||L>xR||R<xL) return INF; if (xL<=L&&R<=xR) return Min[rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return min(query(ls,L,mid,xL,xR),query(rs,mid+1,R,xL,xR)); } void update(int rt,int L,int R,int x,int d){ if (L==R) return (void)(Min[rt]=d); int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; if (x<=mid) update(ls,L,mid,x,d); else update(rs,mid+1,R,x,d); Min[rt]=min(Min[ls],Min[rs]); } int query(int a,int b){ int f1=top[a],f2=top[b],ans=INF; while (f1!=f2){ if (depth[f1]<depth[f2]) swap(f1,f2),swap(a,b); ans=min(ans,query(1,1,n,p[f1],p[a])); a=fa[f1],f1=top[a]; } if (depth[a]>depth[b]) swap(a,b); if (a>outn&&fa[a]!=0) ans=min(ans,query(1,1,n,p[fa[a]],p[fa[a]])); return min(ans,query(1,1,n,p[a],p[b])); } } int main(){ int n=tot=read(),m=read(),q=read(); for (int i=1;i<=n;i++) pr[i]=read(); for (int i=1;i<=m;i++){ int a=read(),b=read(); G[a].push_back(b); G[b].push_back(a); } Tarjan(1); sp :: dfs(1,0,0); sp :: Get_Top(1,1); for (int i=n+1;i<=tot;i++) Mins[i].insert(INF); for (int i=1;i<=n;i++) Mins[sp :: fa[i]].insert(pr[i]); for (int i=1;i<=n;i++) sp :: val[i]=pr[i]; for (int i=n+1;i<=tot;i++) sp :: val[i]=*Mins[i].begin(); sp :: build(1,1,sp :: n=tot); sp :: outn=n; while (q--){ char s[10]; scanf("%s",s); int x=read(),y=read(); if (s[0]==‘A‘) printf("%d\n",sp :: query(x,y)); else { int f=sp :: fa[x]; if (f){ Mins[f].erase(Mins[f].find(pr[x])); Mins[f].insert(pr[x]=y); sp :: update(1,1,sp :: n,sp :: p[f],*Mins[f].begin()); } sp :: update(1,1,sp :: n,sp :: p[x],y); } } return 0; }
UOJ#30/Codeforces 487E Tourists 點雙連通分量,Tarjan,圓方樹,樹鏈剖分,線段樹