tarjan大板子(非讲解):
1、普通缩点DGA
void tarjan(int x){
dfn[x]=low[x]=++cntp;
q.push(x);v[x]=1;
for(int i=head[x];i;i=bi[i].next){
int j=bi[i].to;
if(!dfn[j]){
tarjan(j);
low[x]=min(low[x],low[j]);
}
else if(v[j])low[x]=min(low[x],dfn[j]);
}
if(low[x]==dfn[x]){
int p;
num++;//缩点的个数
do{
p=q.top();
q.pop();
zh[p]=num;//zh[i]表示 i对应的缩点之后的点
cntt[num]++;//cntt[i]表示缩点之后i点代表的点的个数
v[p]=0;
}while(x!=p);
}
}
//重建边 很多题需要缩点重建边成一个DGA以后在进行操作
for(int i=1;i<=cnt;i++){
if(zh[bi[i].fr]!=zh[bi[i].to])ad(zh[bi[i].fr],zh[bi[i].to]);
}
2、求割点
void tarjan(int x,int root){
dfn[x]=low[x]=++cntp;
int fl=0;
for(int i=head[x];i;i=bi[i].next){
int j=bi[i].to;
if(!dfn[j]){
tarjan(j);
low[x]=min(low[x],low[j]);
if(low[j]>=dfn[x]){
fl++;
if(x!=root||fl>1)ge[x]=1;
}
}
else low[x]=min(low[x],dfn[j]);
}
}
3、求割边
void tarjan(int x,int id){
dfn[x]=low[x]=++cntp;
for(int i=head[x];i!=-1;i=bi[i].next){
int j=bi[i].to;
if(!dfn[j]){
tarjan(j,i);
low[x]=min(low[x],low[j]);
if(low[j]>dfn[x])bb[i]=bb[i^1]=1;//注意边要从0或2开始存
}
else if(i!=(id^1))low[x]=min(low[x],dfn[j]);
}
}
4、求点双连通分量
void tarjan(int x,int id){
dfn[x]=low[x]=++cntp;
q.push(x);
for(int i=head[x];i!=-1;i=bi[i].next){
// cout<<i<<' '<<id<<endl;
if(i==(id^1))continue;
int j=bi[i].to;
if(!dfn[j]){
tarjan(j,i);
low[x]=min(low[x],low[j]);
}
else low[x]=min(low[x],dfn[j]);
}
if(dfn[x]==low[x]){和父亲的边是割边,和栈里在他上面的点在一个边双连通分量里面。
num++;
int p;
do{
p=q.top();
q.pop();
zh[p]=num;
}while(p!=x);
}
}
5、点双连通分量
void tarjan(int x){
dfn[x]=low[x]=++cntp;
int fl=0;
for(int i=head[x];i;i=bi[i].next){
int j=bi[i].to;
if(!dfn[j]){
q.push(j);
tarjan(j);
low[x]=min(low[x],low[j]);
if(low[j]>=dfn[x]){
num++;
int p;
do{
p=q.top();
q.pop();
mann[num].ps(p);
}while(p!=j);
mann[num].ps(x);
}
}
else low[x]=min(low[x],dfn[j]);
}
}
END