圆方树(广义圆方树)

圆方树(广义圆方树)

圆方树是个好东西

顾名思义,就是一颗只有圆点(原点)和方点的树

建立它的方法就是将点双中的点全部连向一个方点,然后剪完图后把原边给删掉

然后就会有一些非常奇妙的性质:

  • 每条边连接一个圆点和方点
  • 建出来的图是棵树

然后还有一个点双很奇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;
}
posted @ 2024-02-22 20:47  zhy_learn  阅读(14)  评论(0编辑  收藏  举报