首页 > 其他分享 >点双边双强连通拓展(圆方树)以及一些小技巧

点双边双强连通拓展(圆方树)以及一些小技巧

时间:2023-07-30 22:45:07浏览次数:35  
标签:连通 int ++ back fa 圆方树 ffpos push 双强

点双边双强连通拓展以及一些小技巧

目录

小技巧:

1.关于割点:

点双常常存在割点情况,很难搞,每次dfs都很头疼(不知道割点在哪几个连通块中)
这时候直接每次dfs前手动把bcc内的点都染一个颜色,然后dfs时候看看now和to是不是一个颜色,就可以在一个块中dp了。
这样也可以很快找出块中的边有多少条了!
关键是复杂度还是O(n),非常棒!

2.关于点双和边双的判断技巧:

利用两种图试试你的思路是不是对的,一种是”沙漏五点“型,另一种是”两点一边“型

3.关于自己制造样例的技巧:

第一种:
和2差不多,创建这种图来验证,不多说
第二种:
单点情况、一条链情况、(点双)考虑全是割点的图(中间三角并且各自向外延伸)。

例题:

1.Tourist Reform
https://www.luogu.com.cn/problem/CF732F
https://www.luogu.com.cn/record/118184808

2.poj2942 Knights of the Round Table
关键在于分析:

原题是憎恨图
这里给的是不憎恨图
(我看着咱们亲爱的ljh把原题原来做的copy过样例过了半天错的摸不着头脑哈哈哈哈哈)

很明显的是,一个点双内的点是可以坐在同一桌上的(因为两边都要连人,所以边双不行)
然后为什么是“存在奇环”整个就行呢?
最基本的点双肯定是一个环。然后可以不断拓展“环”达到大的点双
如果一开始的基本环是偶环,后面拓展的也都是偶环
那么很容易发现,能组出的一桌肯定是偶数的人数
那如果一堆里面有奇环,就肯定可以做到奇数个人组成环,然后每个人都至少包含在一个环中

老师说细节多,我觉得主要是割点有点头疼,但有了技巧1就好做多了!
http://222.180.160.110:1024/contest/4027/problem/7

拓展知识

1.广义圆方树:

知识点:

定义:其实就是将一个点双连通分量拆成一棵树
圆方树中的圆代表着原来的所有的点
圆方树中的方则是新加入的节点,对于任意一个点双会有一个“方”链接所有这个点双里的“圆”
注:原来“圆”之间的边在新的圆方树中不用连起来
这么做了以后,可以得到一棵树,满足以下性质:
1.这棵树任意一个边都是圆和方交替连接的
2.圆方树上任意两个“圆”之间的路径,所经过的所有“圆”表明这两个点在原图中想要到达另外一个点需要经过的必经点
3.圆方树上任意两个“圆”之间的路径,所经过的所有“方”,这个“方”所连接的“圆”,表明这两个点在原图中想要到达另外一个点可以选择经过的点

例题:bzoj3331

其实就是利用了上面的性质2。lca,树上差分,点双完全都是板子,very EZ。
代码:



#include<bits/stdc++.h>
using namespace std;
vector<int>bcc[100005];
vector<int>mp[100005];
int dfn[100005],s[100005],low[100005];
bool cut[100005];
int tt,top,cnt;
int ffpos;//方的编号
vector<int>yfs[200005];
void tarjan(int x,int fa){
	tt++;
	low[x]=dfn[x]=tt;
	s[++top]=x;
	int son=0;
	for(int i=0;i<mp[x].size();i++){
		int to=mp[x][i];
		if(to==fa){
			continue;
		}
		if(!dfn[to]){
			son++;
			tarjan(to,x);
			low[x]=min(low[x],low[to]);
			if(low[to]>=dfn[x]){
				cut[x]=1;
				cnt++;
				ffpos++;
				while(s[top+1]!=to){
					bcc[cnt].push_back(s[top]);
					
					yfs[ffpos].push_back(s[top]);
					yfs[s[top]].push_back(ffpos);
					top--;
				}

				bcc[cnt].push_back(x);
				yfs[ffpos].push_back(x);
				yfs[x].push_back(ffpos);
			}
		}
		low[x]=min(dfn[to],low[x]);
	}
	if(son==0&&x==fa){
		cnt++;
		ffpos++;
		yfs[ffpos].push_back(x);
		yfs[x].push_back(ffpos);//至此建立完成圆方树

		bcc[cnt].push_back(x);
	}
	else if(x==fa&&son<=1){
		cut[x]=0;
	}
}
int dep[200005];
int fa[270005][21];//注:2e18=262,144
int treecf[200005];//用于树上差分
void dfs(int x,int faa){//对圆方树进行dfs求出lca进行树上差分
	dep[x]=dep[faa]+1;
	fa[x][0]=faa;
	for(int i=1;i<=18;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];//复习过的倍增算法
	}
	for(int i=0;i<yfs[x].size();i++){
		int to=yfs[x][i];
		if(to==faa){
			continue;
		}
		dfs(to,x);
	}
}
int getlca(int x,int y){
	if(dep[x]>dep[y]){
		swap(x,y);//注意不要swap dep数组了 ,x换到深的那里 
	}//让x在上方 
	for(int i=18;i>=0;i--){//还是一定要记住log2向下取整,所以不一定一次就可以跳到,这时候应该跳离depx最近的,所以从大到小美剧,而且每一次跳的比
	//上一次肯定少,所以一遍i即可 
	//另:从小到大枚举不就相当于一次走一位吗 
		if((dep[y]-(1<<i))>=dep[x]){
			y=fa[y][i];
		}
	}
	if(x==y){
		return x;
	}
	for(int i=18;i>=0;i--){
        if(fa[x][i]==fa[y][i])//因为可能都跳过了 
            continue;
        else{
        	x=fa[x][i];
			y=fa[y][i];
		}
            
    }
    return fa[x][0];
	
}

int ans[200005];
int dfsans(int x,int faa){//差分完后的统计
	int ret=treecf[x];
	for(int i=0;i<yfs[x].size();i++){
		int to=yfs[x][i];
		if(to==faa){
			continue;
		}
		ret+=dfsans(to,x);
	}
	ans[x]=ret;
	return ret;
}
int main(){
	ios::sync_with_stdio(false);
	int n,m,q;
	cin >> n >> m >> q;
	ffpos=n;
	for(int i=1;i<=m;i++){
		int u,v;
		cin >> u >> v;
		mp[u].push_back(v);
		mp[v].push_back(u);
	} 
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			top=0;
			tt=0;
			tarjan(i,i);
		}
	}
	dfs(1,0);//假设1点的fa是0,这样才能做树上差分(不然的话差分fa1=1,导致这里会-2,最后答案为0)
	
	while(q--){
		int u,v;
		cin >> u >> v;
		int lca=getlca(u,v);
		treecf[u]++;
		treecf[v]++;
		treecf[lca]--;
		treecf[fa[lca][0]]--;
		//树上差分
	}
	int k=0;
	k=dfsans(1,0);
	for(int i=1;i<=n;i++){
		cout<< ans[i]<<endl;
	}
	

}


标签:连通,int,++,back,fa,圆方树,ffpos,push,双强
From: https://www.cnblogs.com/linghusama/p/17592233.html

相关文章

  • [算法学习笔记] 强连通分量
    DFS生成树在介绍强连通分量前,我们先来了解一下DFS生成树。一棵DFS生成树分为树边,前向边,返祖边(一说反向边),横叉边。我们来画图解释一下:在这棵DFS生成树中,黑色为树边,它是在DFS遍历时获得的,红色为返祖边,顾名思义,从儿子指向父亲或祖先。蓝色为横叉边,它是在搜索的时候遇到子树中的节......
  • P8436 【模板】边双连通分量 详细讲解
    P8436【模板】边双连通分量概念注意!双连通仅针对无向图而言。割边(桥):删去这条边使图不连通的边。边双连通图:不存在割边的图(等价定义:图中任意两个点都至少两条不同路径可以到达的图)。性质:一个点不可能同时属于2个边双连通图,因为如果两个双连通分量相交与一点,那么删去任......
  • [图论]强连通分量
    强连通分量一、强连通分量1.DFS森林和强连通分(1)DFSForestTreeEdge指树边BackEdge指连向祖先的边(返祖边)ForwardEdge指连向子孙的边(前向边,它主要是在搜索的时候遇到了一个已经访问过的结点,但是这个结点并不是当前结点的祖先。)CrossEdge指连向树其他分支的边(横......
  • 【学习笔记】无向图的连通性
    #割点**定义:**在无向图连通图中,若把点$x$删去后整个图就不连通了,则$x$为割点(割顶)。**朴素方法:**每次删去一个点,然后判断图是否连通,时间复杂度为$O(n(n+m))$。**Tarjan算法:**$dfn_x$:$x$被`dfs`到的时间戳$low_x$:在$x$及以后被搜索的所有节点的$low$值和这些节......
  • Dubbo Triple 协议重磅升级:支持通过 HTTP 连通 Web 与后端微服务
    作者:刘军全新升级的Triple协议在微服务协议选型方面我们看到越来越多的应用从Dubbo2TCP二进制协议迁移到Dubbo3Triple协议(兼容gRPC),以充分利用Triple的高效、全双工、Streaming流式通信模型等能力;Triple+HTTP/2的组合很好的解决了后端服务穿透性等问题,但在阿里及......
  • 点双边双强连通
    点双/边双复习笔记1.点双复习割点:图中的一个点,没有这个点的话,这个图会变成两个图点双:在一个点双内,一个点到另一个点的路径有两条及以上,并且点不会走一样的注意事项:1.割点特判:son<=1且fa=x的不是割点2.环上出发特判:son=0&&fa=x的单独一个作为点双;3.看好大于等于哦!4.low[......
  • ITK 最大圆度连通域提取
    最大圆度概念:圆度计算(Circularity,Roundness)1Roundness=(4*CV_PI*Area)/(Perimeter*Perimeter)2doublegetRoundness(std::vector<cv::Point>contour)3{4doublefactor=(cv::contourArea(contour)*4*CV_PI)/(pow(cv::arcLength(contour,true......
  • [学习笔记] 割点 & 割边 & 双连通分量
    一、定义在无向连通图\(G=(V,E)\)中,若存在一个点\(u(u\inV)\)使得删掉点\(u\)及其相连的边,会使原图不连通,就称\(u\)是原图的一个割点(cutvertex);若存在一条边\((u,v)((u,v)\inE)\)满足删掉\((u,v)\)后会使原图不连通,就称\((u,v)\)是原图的一座桥(或......
  • 挑战程序竞赛系列(74):4.3强连通分量分解(1)
    挑战程序竞赛系列(74):4.3强连通分量分解(1)传送门:POJ2186:PopularCows题意:每头牛都想成为牛群中的红人。给定N头牛的牛群和M个有序对(A,B)。(A,B)表示牛A认为牛B是红人。该关系具有传递性,所以如果A认为B是红人,B认为C是红人,则A认为C是红人。注意:给定的有序对中可能包含(A,B)和(B,C......
  • [学习笔记] 强连通分量
    一、DFSForest从这张经典图说起:在给定的有向图\(G=(V,E)\)内,遍历这张图,其中边分为\(4\)种:树边(treeedge):黑色的边,每一次从\(u\)访问到一个未访问的节点\(v\),则称\((u,v)\)为树边。返祖边(backedge):红色的边,每一次从\(u\)回溯到一个\(u\)的已经访问的祖......