3875. 【NOIP2014八校联考第4场第2试10.20】星球联盟(alliance)

3875. 【NOIP2014八校联考第4场第2试10.20】星球联盟(alliance)

Description

给定一个 n n n 个点的无向图,初始有 m m m 条边,现在依次加入额外 p p p 条边,每次询问加边后,这条边连接的两个点是否处于同一个强连通分量。

n , m ≤ 2 × 10 5 n ,m \le 2 \times 10^5 n,m≤2×105


Solution

别人的 40 pts :边双连通分量

我的 40 pts:怪怪的

就是看这两个点在连这条边之前是否连通,若本来就连通,则加入这条边后才是边双连通的。

用并查集维护连通的点(这里指题目中的连通),方便数个数。

其实发现自己的思路对正解是有一定启发作用的,甚至于现在来想已经是蛮接近的了。只是赛时没有想到可以树边和非树边分开来,离线做。

首先把树边找出来建树,然后对于前面记录下来的非树边进行操作。显然,对于非树边的两个顶点 x , y x,y x,y ,在这两点路径上的点实际上共同组成了一个边双连通分量(即题目中所指的连通),因为所有的点都要归到一起,所以找 lca 这一步递增是不管用的,目前复杂度仍然是 O ( n 2 ) \Omicron(n^2) O(n2) 。

那么我们可以在连边的过程中,直接把当前点指向 lca ,因为他们注定是绑在一个连通块里的,这样后续跳的时候就会节约很多时间,实际上复杂度就降下来了(准确的我也不会证)。

要注意图不一定连通,可能不是树而是森林,所以可以将其统一连向一个父亲,但是这样就要在输入的时候把无解给判掉。

确实是好题,巧妙地把图转化为树,然后通过某些点成为连通块后一直不变且一起贡献的性质,可以极大的优化暴力跳 lca 的过程。这个做法可以积累。

自己能够想到题目所表示的连通要求在连这条边之前已经连通,其实就可以用并查集维护,但是考虑到要算个数,路径上的所有点都要囊括,所以就比较麻烦,直接选择了暴力 dfs 搜,没有继续往深想。这时候其实就容易想到如果使用 dfs 会有很多没有用的尝试,看看能不能一步到位直通过去,所以考虑离线。


Code

c++ 复制代码
#include<cstdio>
#include<iostream>
using namespace std;
const int N=2e5;
struct edge
{
	int to,nxt;
}e[2*N+5];
int lst[N+5],cnt;
int fa[N+5],sz[N+5];
int f[N+5],dep[N+5];
struct n_t_edge
{
	int u,v;
}a[N+5],b[N+5];
int cnta,cntb;
int id[N+5];
int fnd(int x)
{
	return (x==fa[x])?x:fa[x]=fnd(fa[x]);
}
void ad(int x,int y)
{
	e[++cnt]={y,lst[x]};
	lst[x]=cnt; 
}
void dfs(int x,int y)
{
	dep[x]=dep[y]+1,f[x]=y;
	for(int i=lst[x];i;i=e[i].nxt)
	{
		if(e[i].to!=y)
		{
			dfs(e[i].to,x);
		}
	}
}
void unon(int x,int y)
{
	while(x!=y)
	{
		if(dep[x]<dep[y])
		{
			swap(x,y);
		}
//		printf("%d %d %d\n",x,f[x],y);
		int t1=fnd(x),t2=fnd(f[x]);
		if(t1!=t2)
		{
			fa[t1]=t2,sz[t2]+=sz[t1];
		}
		x=t2;
	}
}
int main()
{
	int n,m,p;
	scanf("%d%d%d",&n,&m,&p);
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		int t1=fnd(u),t2=fnd(v);
		if(t1!=t2)
		{
			fa[t1]=t2,ad(u,v),ad(v,u);
		}
		else
		{
			a[++cnta]={u,v};
		}
	}
	for(int i=1;i<=p;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		int t1=fnd(u),t2=fnd(v);
		if(t1!=t2)
		{
			fa[t1]=t2,ad(u,v),ad(v,u);
		}
		else
		{
			b[++cntb]={u,v};
			id[i]=cntb;
		}
	}
	for(int i=2;i<=n;i++)
	{
		int t1=fnd(i),t2=fnd(1);
		if(t1!=t2)
		{
			fa[t1]=t2,ad(1,i);
		}
	}
	dfs(1,0);
//	printf("%d\n",f[18181]);
	for(int i=1;i<=n;i++)
	{
		fa[i]=i,sz[i]=1;
	}
	for(int i=1;i<=cnta;i++)
	{
		unon(a[i].u,a[i].v);
	}
	for(int i=1;i<=p;i++)
	{
		if(!id[i])
		{
			printf("No\n");
		}
		else
		{
			unon(b[id[i]].u,b[id[i]].v);
			printf("%d\n",sz[fnd(b[id[i]].u)]);
		}
	}
	return 0;
}

Thanks

码字不易,请多支持😘

如有疏漏,欢迎评论区留言或者私信,我会及时调整更正。

谢谢啦🍟

相关推荐
真实的菜18 小时前
Redis 从入门到精通(二):深入数据结构 —— 从 RedisObject 到 SkipList 的源码级拆解
数据结构·redis·skiplist
mifengxing18 小时前
LeetCode热题100——字母异位词分组
java·算法·leetcode·职场和发展·哈希表·hot100
Billlly19 小时前
莫比乌斯反演学习笔记
算法
stolentime20 小时前
CF2066D1 Club of Young Aircraft Builders (easy version)题解
c++·算法·动态规划·组合数学
Dillon Dong20 小时前
【风电控制】高低穿现场失败的原因分析——算法简单但工程复杂
算法·变流器·风电控制·dfig
小欣加油20 小时前
leetcode41 缺失的第一个正数
数据结构·c++·算法·leetcode
I Promise3420 小时前
智驾APA_HPA可行驶区域检测算法工程师面试问题整理可参考
算法·面试·职场和发展
智者知已应修善业20 小时前
【51单片机按键控制1分钟正计时倒计时暂停复位】2024-1-2
c++·经验分享·笔记·算法·51单片机
weixin_4684668520 小时前
UNet 模型结构从零搭建与实战解析
人工智能·深度学习·算法·机器学习·ai·unet
努力努力再努力wz20 小时前
【Qt入门系列】一文掌握 Qt 常用显示类控件:QLCDNumber、QProgressBar 与 QCalendarWidget
c语言·开发语言·数据结构·数据库·c++·git·qt