loj#2788-「CEOI2015 Day1」管道【树上差分】

正题

题目链接:https://loj.ac/p/2788


题目大意

给出\(n\)个点\(m\)条边的一张图,求它的所有割边。

\(1\leq n\leq 10^5,1\leq m\leq 6\times 10^6\)内存限制16MB


解题思路

我们存不下所有的边,但是\(n\)很小。一个朴素的想法是我们搞出一棵生成树来,然后对于非树边\((x,y)\)就相当于把\(x\)\(y\)路径上的边都标记成非割边,然后剩下的就是割边了。

但是我们不能离线建生成树,因为我们存不下所有的边,考虑一下别的方向的优化。我们会发现对于非树边来说,如果这一条非树边能被其他非树边完全覆盖,那么说明这条边就没有用,所以我们对于非树边来说也只需要保留一棵最小生成树即可。

然后至于标记方面用树上差分来处理就好了。

时间复杂度:\(O(m+n\log n)\)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cctype>
using namespace std;
const int N=1e5+10;
int n,m,fa[N],Fa[N],c[N],dep[N],f[N][18];
vector<int> G[N];bool v[N];
vector<pair<int,int> >e;
int find(int x)
{return (fa[x]==x)?(x):(fa[x]=find(fa[x]));}
int Find(int x)
{return (Fa[x]==x)?(x):(Fa[x]=Find(Fa[x]));}
int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
void dfs(int x,int fa){
	dep[x]=dep[fa]+1;v[x]=1;
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(y==fa)continue;
		dfs(y,x);f[y][0]=x;
	}
	return;
}
void calc(int x,int fa){
	v[x]=1;
	for(int i=0;i<G[x].size();i++){
		int y=G[x][i];
		if(y==fa)continue;
		calc(y,x);c[x]+=c[y];
	}
	if(!c[x]&&fa)printf("%d %d\n",x,fa);
	return;
}
int LCA(int x,int y){
	if(dep[x]>dep[y])swap(x,y);
	for(int i=17;i>=0;i--)
		if(dep[f[y][i]]>=dep[x])y=f[y][i];
	if(x==y)return x;
	for(int i=17;i>=0;i--)
		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)fa[i]=Fa[i]=i;
	for(int i=1;i<=m;i++){
		int x=read(),y=read();
		if(find(x)!=find(y)){
			fa[find(x)]=find(y);
			G[x].push_back(y);
			G[y].push_back(x);
		}
		else if(Find(x)!=Find(y)){
			Fa[Find(x)]=Find(y);
			e.push_back(make_pair(x,y));
		}
	}
	for(int i=1;i<=n;i++)
		if(!v[i])dfs(i,0);
	memset(v,0,sizeof(v));
	for(int j=1;j<18;j++)
		for(int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	for(int i=0;i<e.size();i++){
		int x=e[i].first,y=e[i].second;
		c[x]++;c[y]++;c[LCA(x,y)]-=2;
	}
	for(int i=1;i<=n;i++)
		if(!v[i])calc(i,0);
	return 0;
}
posted @ 2022-08-11 18:39  QuantAsk  阅读(90)  评论(0编辑  收藏  举报