小小改良,不发题解区了,可是看见题解区两篇几乎完全一样的代码感到非常疑惑。(难道那个是tarjan本人的论文实现?)
/* Code by pp_orange */
#include<bits/stdc++.h>
#define pb push_back
#define repp(i,l,r) for(int i=l;i<=r;++i)
#define pper(i,r,l) for(int i=r;i>=l;--i)
//#define int ll
const int intsz = sizeof(int);
using namespace std;
inline int rd(){
int x(0),f(1);char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while (isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*f;
}
#define MAX 200005
vector<int> v[MAX];
vector<int> iv[MAX];//反图
vector<int> rechk[MAX];
array<int,3> pa[MAX];//<fa,mn,which> , 并查集
int tpa[MAX];// 树上父亲
int ord[MAX];
int dfn[MAX];
int sdom[MAX],idom[MAX];
int sz[MAX];
int tot = 0;
array<int,3> mrg(array<int,3> x,array<int,3> y){// x,y 有顺序,y是x祖先
array<int,3> rlt;
rlt[0] = y[0];
if(y[1]<=x[1])rlt[1] = y[1],rlt[2] = y[2];
// 必须是 <= ,这样我强制钦定是最上面的,和第62行的rechk和初始化搭配使用
else rlt[1] = x[1],rlt[2] = x[2];
return rlt;
}
array<int,3> fd(int x){//并查集
if(x==pa[x][0])return pa[x];
return pa[x] = mrg(pa[x],fd(pa[x][0]));
}
void dfs(int x,int fa){
tpa[x] = fa;
ord[dfn[x] = ++tot] = x;
for(auto to:v[x])if(!dfn[to])dfs(to,x);
return ;
}
signed main(){
int n = rd();
int m = rd();
repp(i,1,m){
int x = rd(),y = rd();
v[x].pb(y);
iv[y].pb(x);
}
sz[1] = 1;idom[1] = 1;
dfs(1,1);
repp(i,1,n)pa[i] = {i,dfn[i],i},sdom[i] = dfn[i];
pper(i,tot,2){
int nw = ord[i];
for(auto to:rechk[nw])idom[to] = fd(to)[2];// 在父节点统一重新检测,题解里面都是边跑边重检测父亲,还要清空,非常麻烦
for(auto to:iv[nw]){
if(!dfn[to])continue;
sdom[nw] = min(sdom[nw],fd(to)[1]);
}
pa[nw][0] = tpa[nw];pa[nw][1] = sdom[nw];
idom[nw] = sdom[nw] = ord[sdom[nw]];
rechk[sdom[nw]].pb(nw);
}
repp(i,2,tot){
int nw=ord[i];
if(sdom[nw]!=idom[nw])idom[nw] = idom[idom[nw]];
}
pper(i,tot,2){
int nw=ord[i];
sz[idom[nw]] += ++sz[nw];
}
repp(i,1,n)cout<<sz[i]<<' ';cout<<endl;
return 0;
}
注释写代码了,我个人认为这样写边界少很多,也没有特别多的数组嵌套,代码也比较短。