【学习笔记】圆方树(CF487E Tourists)

终于学了圆方树啦~\(≧▽≦)/~

感谢y_immortal学长的博客和帮助 把他的博客挂在这里~ 点我传送到巨佬的博客QwQ!

 

首先我们来介绍一下圆方树能干什么呢qwq

1.将图上问题简化到树上问题

2.一般是路径并

3.资磁修改!

然后我们就可以步入正题来学习圆方树啦~

 

——超良心圆方树教程!——

这里是一个前缀芝士清单!

1.Tarjan求点双连通分量

2.树链剖分

如果你大体知道这两个东西在干什么 那你看接下来的教程就会非常熟练【大雾

 

一。圆方树的构造

原图中的点称为原点 新建的点称为方点

对于一个无向图,我们取它所有的极大点双连通分量,把这些点之间的边全部删掉,然后新建一个点与分量中的点连边。

是不是很云里雾里 不着急 我们来画个图【其实是我偷的猫锟的图T^T】

橙色的就是原图圆点 蓝色的就是新建的方点

我们发现 原图中的圆点点双代表的路径 全部都可以被方点表示啦!

也就是说两个点x,y之间的路径并全部都可以被圆点方点表示呢qwq

圆方树可不止这一个性质呢

1.任意两个圆点不会相邻,任意两个方点也不会相邻

2.对于两个方点之间的圆点,是两个点双的公共点,即割点

3.对于两个点x,y,它们的树上简单路径上的圆点都是割点(必经点),路径也是原图x,y之间的路径并

这个过程有一个小细节要注意,就是一个圆点可能会出现在多个点双里,所以在tarjan弹栈的时候不能把当前节点弹出来的qwq

圆方树就讲完啦~ 是不是很简单!接下来就可以刚题啦/xyx

 

————一道例题————

CF487E Tourists

没想到4年前就已经有圆方树了呢qwq

题意:给定一张无向图,求两点之间路径最小值的最小值,带修。

两点之间路径并!圆方树!

对于这个题,我们可以轻松地建出模型。

对原图建立圆方树,查询树上两个点之间的路径最小值。

每个方点维护它所连接的所有圆点的权值。

但是问题来了,如果出现菊花图,一个圆点的修改可能会影响到O(n)级别的方点

然后就萎啦QAQ

我们对这个东西进行优化 对于一个方点 我们可以开一个multiset维护它第一层儿子的权值,然后每次修改一个圆点,只会影响到一个方点,这样的话时间复杂度是O(nlg^2n)可以接受的。

然后你就写呀写。交上去。WA了!!!你正准备喷笔者。不要着急好不好!我们还有一个小问题没处理呢。

我们发现原先一个方点维护的是它连接的所有圆点,但是我们现在维护的是它的儿子的权值。这之间是不是有一点小出入呢。

没错,当x,y的lca为一个方点时,我们少维护了一个点,就是方点上方的圆点,这个圆点也是属于这个点双的,也是可以被经过的,所以,你只需要加一个小细节,对于LCA时方点的,答案还需要和方点上方的圆点取最小值。

这样就做完啦~

码量还好呢qwq

放代码啦。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
#include<set>
#define inf 2002122500
#define ll long long
#define mxn 100010
#define ls x<<1
#define rs x<<1|1
using namespace std;

struct edge{int to,lt;}e[mxn<<2],p[mxn<<1];
int in[mxn<<1],ip[mxn],cnt,cnp,v[mxn<<1],n,m;
int dfn[mxn<<1],low[mxn<<1],tot,dep[mxn<<1];
int son[mxn<<1],sz[mxn<<1],top[mxn<<1],fa[mxn<<1],xl[mxn<<1];
int num;stack<int> st;
multiset<int> sq[mxn];
struct SegTree
{
	int mn[mxn<<4];
	void pushup(int x){mn[x]=min(mn[ls],mn[rs]);}
	void build(int x,int l,int r)
	{
		if(l==r){mn[x]=v[xl[l]];return;}
		int mid=l+r>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		pushup(x);
	}
	void modify(int x,int l,int r,int d,int val)
	{
		if(l==r){mn[x]=val;return;}
		int mid=l+r>>1;
		if(d<=mid)	modify(ls,l,mid,d,val);
		else	modify(rs,mid+1,r,d,val);
		pushup(x);
	}
	int query(int x,int l,int r,int LL,int RR)
	{
		if(l>=LL&&RR>=r)	return mn[x];
		int mid=l+r>>1,ans=inf;
		if(LL<=mid)	ans=min(ans,query(ls,l,mid,LL,RR));
		if(RR>mid)	ans=min(ans,query(rs,mid+1,r,LL,RR));
		return ans;
	}
}T;
void app(int x,int y)
{
	p[++cnp].to=y;p[cnp].lt=ip[x];ip[x]=cnp;
	p[++cnp].to=x;p[cnp].lt=ip[y];ip[y]=cnp;
}
void add(int x,int y)
{
	e[++cnt].to=y;e[cnt].lt=in[x];in[x]=cnt;
	e[++cnt].to=x;e[cnt].lt=in[y];in[y]=cnt;
}
void tarjan(int x,int ff)
{
	dfn[x]=low[x]=++tot;st.push(x);
	for(int i=ip[x];i;i=p[i].lt)
	{
		int y=p[i].to;if(y==ff)	continue;
		if(!dfn[y])
		{
			tarjan(y,x);
			low[x]=min(low[y],low[x]);
			if(low[y]>=dfn[x])
			{
				int cur;++num;
				add(num,x);
				do
				{
					cur=st.top();st.pop();
					add(num,cur);
				}while(cur!=y);
			}
		}
		else	low[x]=min(low[x],dfn[y]);
	}
}
void dfs(int x)
{
	sz[x]=1;
	for(int i=in[x];i;i=e[i].lt)
	{
		int y=e[i].to;if(y==fa[x])	continue;
		dep[y]=dep[x]+1;fa[y]=x;dfs(y);sz[x]+=sz[y];
		if(sz[y]>sz[son[x]])	son[x]=y;
		if(x>n)	sq[x-n].insert(v[y]);
	}
	if(x>n)	v[x]=*sq[x-n].begin();
}
void dfs2(int x,int tt)
{
	top[x]=tt;dfn[x]=++tot;xl[tot]=x;
	if(!son[x])	return;
	dfs2(son[x],tt);
	for(int i=in[x];i;i=e[i].lt)
	{
		int y=e[i].to;
		if(y==fa[x]||y==son[x])	continue;
		dfs2(y,y);
	}
}
int ask(int x,int y)
{
	int ans=inf;
	while(top[x]!=top[y])
	{
		if(dep[top[y]]>dep[top[x]])	swap(x,y);
		ans=min(ans,T.query(1,1,num,dfn[top[x]],dfn[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])	swap(x,y);
	//printf("QWQ");
	//printf("%d %d %d %d\n",x,y,dfn[x],dfn[y]);
	ans=min(ans,T.query(1,1,num,dfn[x],dfn[y]));
	if(x>n)	ans=min(ans,v[fa[x]]);
	return ans;
}
void change(int x,int val)
{
	if(fa[x])	sq[fa[x]-n].erase(v[x]),sq[fa[x]-n].insert(val),v[fa[x]]=*sq[fa[x]-n].begin();
	v[x]=val;T.modify(1,1,num,dfn[x],v[x]);
	if(fa[x])	T.modify(1,1,num,dfn[fa[x]],v[fa[x]]);
}
int main()
{
	int x,y,q;char ch[5];
	scanf("%d%d%d",&n,&m,&q);num=n;
	for(int i=1;i<=n;i++)	scanf("%d",&v[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		app(x,y);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i])	tarjan(i,i);
	tot=0;
	for(int i=1;i<=n;i++)
		if(!dep[i])	dep[i]=1,dfs(i),dfs2(i,i);
	T.build(1,1,num);
	for(int i=1;i<=q;i++)
	{
		scanf("%s%d%d",ch,&x,&y);
		if(ch[0]=='C')	change(x,y);
		else printf("%d\n",ask(x,y));
	}
	return 0;
}

完结撒花ヾ(o◕∀◕)ノヾ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值