1. 程式人生 > >UOJ#30/Codeforces 487E Tourists 點雙連通分量,Tarjan,圓方樹,樹鏈剖分,線段樹

UOJ#30/Codeforces 487E Tourists 點雙連通分量,Tarjan,圓方樹,樹鏈剖分,線段樹

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,圓方樹,樹鏈剖分,線段樹