tarjan板子(割點割邊連通分量)
阿新 • • 發佈:2019-02-13
low函式實際上可以不開成陣列的(話是這麼說,但是會佔用棧空間,所以說還是老老實實寫成靜態陣列開在外面比較好)。
割點:
void tarjan_point(int i,int fd)//給邊上標號來判定重邊
{
dfn[i]=low[i]=++dfs_clock;
int j,id,chd=0;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to,id=E[p].id;
if(dfn[j])
{
if(dfn[j]<dfn[i]&&id !=fd) low[i]=min(low[i],dfn[j]);//利用返祖邊更新low函式
continue;
}
chd++;
tarjan_point(j,id);
if(low[j]>=dfn[i]) mark[i]=1;//只要i有一個兒子滿足lowj>=dfni就說明i是割點
low[i]=min(low[i],low[j]);//利用樹邊更新low函式
}
if(fd==0&&chd==1) mark[i]=0;//對於開始遍歷的根結點需要特殊判斷,可能這個點不是割點
}
割邊:
void tarjan_edge(int i,int fd)//給邊上標號來判定重邊
{
dfn[i]=low[i]=++dfs_clock;
int j,id;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to,id=E[p].id;
if(dfn[j])
{
if(dfn[j]<dfn[i]&&id!=fd) low[i]=min(low[i],dfn[j]);//利用返祖邊更新low函式
continue ;
}
tarjan_edge(j,id);
low[i]=min(low[i],low[j]);//和兒子的連邊更新low函式
if(low[j]==dfn[j])
{
a[++cnt]=(data){min(i,j),max(i,j)};
mark[id]=1;//改成判定這一條指向兒子的邊是不是割邊,此寫法也不受重邊的影響。
}
}
}
邊雙連通分量:
void tarjan_edge(int i,int fd)
{
dfn[i]=low[i]=++dfs_clock;
stk[++top]=i;
int j,id;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to,id=E[p].id;
if(dfn[j])
{
if(dfn[j]<dfn[i]&&id!=fd) low[i]=min(low[i],dfn[j]);
continue;
}
tarjan_edge(j,id);
low[i]=min(low[i],low[j]);
if(low[j]==dfn[j])//第一次發現割邊就應該算成一個邊雙連通分量
{
mark[id]=1;
ebc_cnt++;
while(1)
{
int x=stk[top--];
ebc[ebc_cnt]++;
ebcno[x]=ebc_cnt;
if(x==j) break;
}
MAX_ebc=max(MAX_ebc,ebc[ebc_cnt]);
}
}
if(!fd&&top)//改了寫法之後需要特殊處理遍歷樹的根結點
{
ebc_cnt++;
while(top)
{
int x=stk[top--];
ebc[ebc_cnt]++;
ebcno[x]=ebc_cnt;
}
MAX_ebc=max(MAX_ebc,ebc[ebc_cnt]);
}
}
點雙連通分量(割點的分量標號沒什麼意義):
void tarjan_point(int i,int fd)
{
dfn[i]=low[i]=++dfs_clock;
stk[++top]=i;
int j,id,chd=0;
for(int p=first[i];p;p=E[p].next)
{
j=E[p].to,id=E[p].id;
if(dfn[j])
{
if(dfn[j]<dfn[i]&&fd!=id) low[i]=min(low[i],dfn[j]);
continue;
}
chd++;
tarjan_point(j,id);
low[i]=min(low[i],low[j]);
if(low[j]>=dfn[i])//注意判定條件和邊雙連通分量的區別
{
mark[i]=1;
bcc_cnt++;
bcc[bcc_cnt]++;
bccno[i]=0;
while(1)
{
int x=stk[top--];
bcc[bcc_cnt]++;
bccno[x]=bcc_cnt;
if(x==j) break;
}
MAX_bcc=max(MAX_bcc,bcc[bcc_cnt]);
}
}
if(fd==0&&chd==1) mark[i]=0;
}
強連通分量:
void tarjan_scc(int i)
{
dfn[i]=low[i]=++dfs_clock;
stk[++top]=i;
for(int p=first[i];p;p=E[p].next)
{
int j=E[p].to;
if(dfn[j])
{
if(!sccno[j]) low[i]=min(low[i],dfn[j]);//反向邊更新low函式
continue;
}
tarjan_scc(j);
low[i]=min(low[i],low[j]);
}
if(low[i]==dfn[i])
{
scc_cnt++;
while(1)
{
int x=stk[top--];
sccno[x]=scc_cnt;
if(x==i) break;
}
}
}