圓方樹總結 [uoj30]Tourists


圓方樹總結

所謂圓方樹就是把一張圖變成一棵樹。
怎么變啊qaq
這里盜一張圖

簡單來說就是給每一個點雙新建一個點,然后連向這個點雙中的每一個點。特殊的,把兩個點互相連通的也視作一個點雙。

我們把原來就有的點稱作圓點,因點雙而新建的點稱之為方點。
這樣這棵圓方樹就會有一個這樣的性質:和每個圓點(方點)相連的點一定是方點(圓點)。

我們在每個圓點上維護這個點原本的信息,在方點上維護這個點雙的信息,這樣就能完成一些關於一般圖的所有簡單路徑的詢問了。

例如:我現在有一張一般圖,每個點有一個點權,要求從\(u\)\(v\)的所有簡單路徑中經過的最小點權是多少。

可以建出圓方樹,在每個方點上維護這個點雙中的最小點權,那么每次詢問就是查詢一個路徑最小值了。

如果有修改怎么辦呢?直接改完圓點后改和它相鄰的方點?
顯然這個復雜度是\(O(度數)\)的,很容易卡成\(O(n^2)\)

方法:每個方點維護的信息中不包括它的父親圓點,這樣修改圓點的時候就只需要修改它的父親方點。查詢的時候如果路徑的\(lca\)是個方點,就還要再算上它的父親圓點。

是不是很妙?

uoj30
就是上面講的那個東西。
圓方樹+可刪除堆+樹鏈剖分+線段樹

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 4e5+5;
int n,tot,m,q,val[N],dfn[N],low[N],tim,S[N];
int fa[N],dep[N],sz[N],son[N],top[N],mn[N<<2];
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[N];
struct Graph{
	int to[N],nxt[N],head[N],cnt;
	void link(int u,int v){
		to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
		to[++cnt]=u;nxt[cnt]=head[v];head[v]=cnt;
	}
}G1,G2;
void Tarjan(int u){
	dfn[u]=low[u]=++tim;S[++S[0]]=u;
	for (int e=G1.head[u];e;e=G1.nxt[e]){
		int v=G1.to[e];
		if (!dfn[v]){
			Tarjan(v),low[u]=min(low[u],low[v]);
			if (low[v]>=dfn[u]){
				G2.link(++tot,u);int x=0;
				do{
					x=S[S[0]--];G2.link(tot,x);
				}while (x!=v);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
}
void dfs1(int u,int f){
	fa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
	if (u<=n&&f) Q[f].insert(val[u]);
	for (int e=G2.head[u];e;e=G2.nxt[e]){
		int v=G2.to[e];if (v==f) continue;
		dfs1(v,u);sz[u]+=sz[v];
		if (sz[v]>sz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int up){
	top[u]=up;dfn[u]=++tim;
	if (son[u]) dfs2(son[u],up);
	for (int e=G2.head[u];e;e=G2.nxt[e]){
		int v=G2.to[e];if (v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
void modify(int x,int l,int r,int p,int v){
	if (l==r) {mn[x]=v;return;}
	int mid=l+r>>1;
	if (p<=mid) modify(x<<1,l,mid,p,v);
	else modify(x<<1|1,mid+1,r,p,v);
	mn[x]=min(mn[x<<1],mn[x<<1|1]);
}
int query(int x,int l,int r,int ql,int qr){
	if (l>=ql&&r<=qr) return mn[x];
	int mid=l+r>>1;
	if (qr<=mid) return query(x<<1,l,mid,ql,qr);
	if (ql>mid) return query(x<<1|1,mid+1,r,ql,qr);
	return min(query(x<<1,l,mid,ql,qr),query(x<<1|1,mid+1,r,ql,qr));
}
int main(){
	tot=n=gi();m=gi();q=gi();
	for (int i=1;i<=n;++i) val[i]=gi();
	while (m--){
		int u=gi(),v=gi();
		G1.link(u,v);
	}
	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) modify(1,1,tot,dfn[i],val[i]);
	for (int i=n+1;i<=tot;++i) modify(1,1,tot,dfn[i],Q[i].top());
	while (q--){
		char ch='%';while (ch!='A'&&ch!='C') ch=getchar();
		if (ch=='C'){
			int a=gi(),b=gi();
			if (fa[a]) Q[fa[a]].erase(val[a]);
			val[a]=b;modify(1,1,tot,dfn[a],val[a]);
			if (fa[a]) Q[fa[a]].insert(val[a]),modify(1,1,tot,dfn[fa[a]],Q[fa[a]].top());
		}
		else{
			int u=gi(),v=gi(),ans=2e9;
			while (top[u]^top[v]){
				if (dep[top[u]]<dep[top[v]]) swap(u,v);
				ans=min(ans,query(1,1,tot,dfn[top[u]],dfn[u]));
				u=fa[top[u]];
			}
			if (dep[u]>dep[v]) swap(u,v);
			ans=min(ans,query(1,1,tot,dfn[u],dfn[v]));
			if (u>n) ans=min(ans,val[fa[u]]);
			printf("%d\n",ans);
		}
	}
	return 0;
}

圓方樹與仙人掌

留坑待補


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM