正题
题目链接:https://loj.ac/p/2788
题目大意
给出nnn个点mmm条边的一张图,求它的所有割边。
1≤n≤105,1≤m≤6×1061\leq n\leq 10^5,1\leq m\leq 6\times 10^61≤n≤105,1≤m≤6×106,内存限制16MB
解题思路
我们存不下所有的边,但是nnn很小。一个朴素的想法是我们搞出一棵生成树来,然后对于非树边(x,y)(x,y)(x,y)就相当于把xxx到yyy路径上的边都标记成非割边,然后剩下的就是割边了。
但是我们不能离线建生成树,因为我们存不下所有的边,考虑一下别的方向的优化。我们会发现对于非树边来说,如果这一条非树边能被其他非树边完全覆盖,那么说明这条边就没有用,所以我们对于非树边来说也只需要保留一棵最小生成树即可。
然后至于标记方面用树上差分来处理就好了。
时间复杂度:O(m+nlogn)O(m+n\log n)O(m+nlogn)
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;
}