zoukankan      html  css  js  c++  java
  • 【学习笔记】圆方树(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
    ",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
    ",ask(x,y));
    	}
    	return 0;
    }

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

  • 相关阅读:
    第一章:linux命令初步
    请教shell读写XML问题(转)
    讓 BootCamp 下的 Windows XP 也能有 D 硬碟槽(转)
    Linux下的多线程编程(转)
    怎么查看redhat版本
    不透過 Boot Camp 安裝 Windows 7,並切割成多個磁碟槽(转)
    同位语从句用法详解
    更改linux的最大文件描述符限制
    ObjectiveC中 copy, tetain, assign , readonly , readwrite, nonatomic区别
    Linux如何查找文件安装路径
  • 原文地址:https://www.cnblogs.com/hanyuweining/p/10321925.html
Copyright © 2011-2022 走看看