连通性相关
前置知识
一个结点 的 DFS 序是指深度优先搜索遍历时改结点被搜索的次序,简记为 dfn\(x\)。
Tarjan 缩点维护两个东西,一个是 DFS 序,一个是 Low 值,开两个数组,dfn和low。
dfn:dfs序。
low:该节点能够追溯到的最小的dfn。
设当前枚举点为u ,连出去的边为v 。
- v未被访问:继续对 v进行深度搜索。在回溯过程中,用low\(v\) 更新low\(u\)。因为存在从u到v 的直接路径,所以v能够回溯到的已经在栈中的结点,u也一定能够回溯到。
- v被访问过,已经在栈中:即已经被访问过,根据 low值的定义,则用dfn\(v\)更新low\(u\) 。
在回溯的过程中,判定low\(u\)==dfn\(u\)的条件是否成立,如果成立,则从栈中取出一个点,处理它所在的强连通分量的编号以及大小,也可以处理其他的一些操作,这样直到把所有点处理完为止。
void tarjan(int x){
low[x]=dfn[x]=++tot;
sta[++sc]=x;vis[x]=1;
for(int i=head[x];i;i=a[i].next ){
int now=a[i].v ;
if(!dfn[now])tarjan(now),low[x]=min(low[x],low[now]);
else if(vis[now])
low[x]=min(low[x],dfn[now]);
}
if(low[x]==dfn[x]){
int Top=sta[sc--];vis[Top]=0;(Num[Top]=++t;……根据题目添加
while(Top!=x){
Top=sta[sc--];
vis[Top]=0;
Num[Top]=t;
}
}
}
简写版:
dfn[u]=low[u]=++idx,vis[u]=true;stc[++sc]=u;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
else if(vis[v]) low[u]=min(dfn[v],low[u]);
}
if(dfn[u]==low[u]){
int Top=stc[sc--];vis[Top]=0,……;
while(Top!=u) Top=stc[sc--],vis[Top]=0,……;
}
割点 & 割边(桥)
割点和点双是针对无向图来说的,所以我们重新定义 ,为不经过父节点能追溯到的最
小的 。
根据low 和 dfn的定义:得出判断割点(非根节点)的判断条件:low\(v\)>=dfn\(u\)
意思是:不走父节点,无法追溯到父节点以前的节点,那u一定是割点。
换句话说,u节点把v“卡住了”。
以上是针对非根节点来说的,如果是根节点,只需要判断它的子树是否大于 棵即可,原因是如果断开根节点,子树必定不连通。
void tarjan(int x,int fa,int root){
dfn[x]=low[x]=++tot;
int ch=0;
for(int i=head[x];i;i=a[i].next ){
int now=a[i].v ;
if(!dfn[now]){
ch++;
tarjan(now,x,root);
low[x]=min(low[x],low[now]);
if(low[now]>=dfn[x]&&x!=root) f[x]=1;
}
else if(now!=fa) low[x]=min(dfn[now],low[x]);//不是根节点 (判断割点(非根节点)的判断条件:low[v]>=dfn[u])
}
if(x==root&&ch>=2) f[x]=1;//是根节点的情况 子树大于1
}
割边和点双的区别为\(low[v] \geq dfn[u]\) --> \(low[v]> dfn[u]\)
就是不经过父节点没有别的路可以回去,那\(u\)和\(fa\)之间一定是割边。
void tarjan(int x,int fa){
low[x]=dfn[x]=++tot;
for(int i=head[x];i;i=a[i].next ){
int v=a[i].v ;
if(!dfn[v]){
tarjan(v,x),low[x]=min(low[x],low[v]);
if(dfn[u]<low[v]) G++;
}
else if(vis^fa)
low[x]=min(low[x],low[now]);
}
}
点/边双连通分量
在一张连通的无向图中,对于两个点 \(u\) 和 \(v\),如果无论删去哪条边(只能删去一条)都不能使它们不连通,我们就说 \(u\) 和 \(v\) 边双连通。
在一张连通的无向图中,对于两个点 \(u\) 和 \(v\),如果无论删去哪个点(只能删去一个,且不能删 \(u\) 和 \(v\) 自己)都不能使它们不连通,我们就说 \(u\) 和 \(v\) 点双连通。
边双连通具有传递性,即,若 \(x\) , \(y\) 边双连通,\(y\) , \(z\) 边双连通,则 \(x\) , \(z\) 边双连通。
点双与割点的区别是不需要判\(root\),需要同样满足\(low[v] \ge dfn[u]\)。
求点双分量,可以在求割点的时候顺便求出来。
当我们发现 \(low[v]\ge dfn[u]\) 时,就说明 \(v\) 及其子树(加上 \(u\))一起形成了一个点双。
此时,我们将 栈内的点一直pop,pop 到 \(v\) 为止(包括 \(v\)),但是 不包括 \(u\)。这是因为 \(u\) 作为割点,可能还属于别的点双分量,之后还要用到。
• 求点双时,我们并不关心 \(u\) 本身是否为割点,只要出现了 \(low[v]\ge dfn[u]\) ,就说明出现了一个新点双。(例如,在只有 \(1,2\) 这两个点的情况下,不存在割点,但是 \(1,2\) 仍然是一个点双)。
cofe:
bool cut[maxn];
vector<int> dcc_list[maxn];
void dfs(int u, int p) {
dfn[u] = low[u] = ++id;
st[++tail] = u;
int child = 0;
for (int e = head[u]; e; e = edges[e].nxt) {
int v = edges[e].to;
if (v == p) continue;
if (dfn[v]) {
low[u] = min(low[u], dfn[v]);
} else {
child++;
dfs(v, u);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
from[v] = ++dcc;
while (st[tail] != v) {
int cur = st[tail--];
from[cur] = from[v];
dcc_list[dcc].push_back(cur);
}
tail--;
dcc_list[dcc].push_back(v);
dcc_list[dcc].push_back(u);
}
if (low[v] >= dfn[u] && p) cut[u] = 1; // 割点(非根节点)
}
}
if (!p && child >= 2) cut[u] = 1; // 割点(根节点)
}
const int N=5e5+10;
int n,m,x,y,cnt=-1,w[N],head[N],xo[N];
int tot,dfn[N],low[N],qiao[N<<1],TOT,Dfn[N];
struct node{
int u,v,nxt;
}a[N<<1];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
il int Max(int x, int y) {return x > y ? x: y;}
il int Min(int x, int y) {return x < y ? x: y;}
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void tarjan(int x,int fa){
dfn[x]=low[x]=++tot;
for(int i=head[x];i!=-1;i=a[i].nxt){
int v=a[i].v;
if(!dfn[v]){//再往下找的话才可能low[v]>low[x],既然已经走过了,那low[v]必然小于low[x]
tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>low[x]){//正反两个边都标记为桥
qiao[i]=1;
qiao[i^1]=1;
}
}
else if(v^fa) low[x]=min(low[x],low[v]);
}
}
void dfs(int x){
Dfn[x]=TOT;
xo[TOT]^=w[x];
for(int i=head[x];i!=-1;i=a[i].nxt){
int v=a[i].v;
if(qiao[i]) continue;
if(!Dfn[v])dfs(v);
}
}
signed main(){
n=read();m=read();
cl(head,-1);
rep(i,1,n) w[i]=read();
rep(i,1,m){
x=read();y=read();
add(x,y);
add(y,x);
}
rep(i,1,n) if(!dfn[i]) tarjan(i,0);
rep(i,1,n) if(!Dfn[i]) ++TOT,dfs(i);
sort(xo+1,xo+1+TOT);
rep(i,1,TOT) cout<<xo[i]<<'\n';
return 0;
}
const int N=5e5+10;
int n,m,x,y,cnt=-1,head[N];
int tot,TOT,Cnt,dfn[N],low[N],Dfn[N],qiao[N<<1];
struct node{
int u,v,nxt;
}a[N<<1];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
il int Max(int x, int y) {return x > y ? x: y;}
il int Min(int x, int y) {return x < y ? x: y;}
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void tarjan(int x,int fa){
dfn[x]=low[x]=++tot;
for(int i=head[x];i!=-1;i=a[i].nxt){
int v=a[i].v;
if(!dfn[v]){
tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>low[x]){
qiao[i]=1;
qiao[i^1]=1;
}
}
else if(v^fa) low[x]=min(low[x],low[v]);
}
}
void dfs(int x){
Dfn[x]=TOT;
for(int i=head[x];i!=-1;i=a[i].nxt){
if(qiao[i]) continue;
if(!Dfn[a[i].v]) dfs(a[i].v);
}
}
signed main(){
n=read();m=read();
cl(head,-1);
rep(i,1,m){
x=read();y=read();
add(x,y);
add(y,x);
}
rep(i,1,n) if(!dfn[i]) tarjan(i,0);
rep(i,1,n) if(!Dfn[i]) ++TOT,dfs(i);
cout<<TOT;
return 0;
}
const int N=2e6+10;
int n,m,x,y,cnt,head[N];
int tot,sc,Dcc,low[N],dfn[N],sta[N];
vector<int>dcc[N];
struct node{
int u,v,nxt;
}a[N<<1];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
il int read(){
int f = 1, x = 0; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-')f = -f;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = x*10 + ch-'0';ch = getchar();}
return f * x;
}
void tarjan(int x,int fa){
dfn[x]=low[x]=++tot;sta[++sc]=x;
int ch=0;
for(int i=head[x];i;i=a[i].nxt){
int v=a[i].v;
if(!dfn[v]) {
++ch;
tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x]){
++Dcc;
int Top=sta[sc--];
while(Top!=v) dcc[Dcc].push_back(Top),Top=sta[sc--];
dcc[Dcc].push_back(v);
dcc[Dcc].push_back(x);
}
}
else if(v^fa) low[x]=min(low[x],low[v]);
}
if(!fa&&!ch) dcc[++Dcc].push_back(x);
}
signed main(){
n=read();m=read();
rep(i,1,m){
x=read();y=read();
add(x,y);
add(y,x);
}
rep(i,1,n) if(!dfn[i]) tarjan(i,0);
cout<<Dcc<<'\n';
rep(i,1,Dcc){
int l=dcc[i].size();
cout<<l<<" ";
rep(j,0,l-1)cout<<dcc[i][j]<<' ';
cout<<'\n';
}
return 0;
}
const int N=1e5+10;
int n,m,x,y,Dcc,cnt,head[N];
int sc,tot,low[N],dfn[N],sta[N];
vector<int>dcc[N];
struct node{
int u,v,nxt;
}a[N<<1];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
void tarjan(int x,int fa){
low[x]=dfn[x]=++tot;sta[++sc]=x;
int ch=0;
for(int i=head[x];i;i=a[i].nxt){
int v=a[i].v;
if(!dfn[v]){
++ch;
tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x]){
++Dcc;
int Top=sta[sc--];
while(Top!=v){
dcc[Dcc].push_back(Top);
Top=sta[sc--];
}
dcc[Dcc].push_back(v);
dcc[Dcc].push_back(x);
}
}
else if(v^fa) low[x]=min(low[x],low[v]);
}
if(!fa&&!ch) dcc[++Dcc].push_back(x);
}
signed main(){
n=read();m=read();
rep(i,1,m){
x=read();y=read();
add(x,y);
add(y,x);
}
rep(i,1,n) if(!dfn[i]) tarjan(i,0);
rep(i,1,Dcc){
int l=dcc[i].size();
rep(j,0,l-1) cout<<dcc[i][j]<<" ";
cout<<'\n';
}
return 0;
}
最近脑袋有些浑浊,所以这当然不完全是我自己写的。
const int N=2e6+10;
int t,n,x,y,a1,a2,T,cnt,head[N];
int sc,tot,ctot,Mt,Dcc,dfn[N],low[N],sta[N],cut[N];//cut record gd
vector<int>dcc[N];
struct node{
int u,v,nxt;
}a[N];
void add(int u,int v){
a[++cnt]=(node){u,v,head[u]};
head[u]=cnt;
}
void clean(){
cl(dfn,0);
cl(low,0);
cl(dcc,0);
cl(cut,0);
cl(head,0);
// sc=0;
sc=-1;
tot=0;Dcc=0;Mt=0;
cnt=0;Dcc=0;a1=0;a2=1;
}
void tarjan(int x,int fa){
dfn[x]=low[x]=++tot;sta[++sc]=x;
int ch=0;
for(int i=head[x];i;i=a[i].nxt){
int v=a[i].v;
if(!dfn[v]){
++ch;
tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x]) {//Wrong
++Dcc;
int Top=sta[sc--];
while(Top!=v){
dcc[Dcc].pb(Top);
Top=sta[sc--];
}
dcc[Dcc].pb(v);
dcc[Dcc].pb(x);
}
if(low[v]>=dfn[x]&&fa) cut[x]=1;
}
else if(v^fa) low[x]=min(low[x],dfn[v]);
}
if(!fa&&ch>=2) cut[x]=1;
}
signed main(){
n=read();
while(n){
++T;
clean();
rep(i,1,n){
x=read();y=read();
Mt=Max(Max(x,y),Mt);
add(x,y);
add(y,x);
}
rep(i,1,Mt) if(!dfn[i]) tarjan(i,0);
rep(i,1,Dcc){
int cut_c=0;
int sz=dcc[i].size();
rep(j,0,dcc[i].size()-1){
if(cut[dcc[i][j]]) ++cut_c;
}
if(cut_c>=2)continue;
if(cut_c==1) ++a1,a2*=(ll)(sz-1);
if(cut_c==0) a1+=2,a2*=(ll)(sz)*(ll)(sz-1)/2;
}
cout<<"Case "<<T<<": "<<Max(a1,2)<<" "<<a2<<'\n';
n=read();
}
return 0;
}

浙公网安备 33010602011771号