还是挺简单的tarjan。
判断时可能重复,直接bitset搞定。
首先tarjan缩点,每个scc的内部肯定能互相到达,更一下,而且一个scc里的各个点的贡献肯定是一样的,topsort,更新答案就可以了,用bitset的count成上scc大小即可。
据说数据很水,大约O(n^3)可过,但是我看了看,没缩点,直接dfs,细节比较多,略烦,上述算法比较长,但是脑量比较小。还是比较好的,tarjan和topsort打得熟就15min搞定。
#include<iostream> #include<algorithm> #include<cmath> #include<cstring> #include<cstdio> #include<vector> #include<queue> #include<stack> #include<bitset> #include<map> using namespace std; struct EDGE{ int ed,nex; }edge[4000050],edgec[4000050]; int num,numc,ans,first[3000],firstc[3000]; int n,ord,sccnum,top; int sta[1000000],dfn[3000],low[3000],bl[3000],du[3000]; char ch[3000]; bool ins[3000]; vector<int>scc[3000]; bitset<3000>s[3000]; void add(int st,int ed){ // cout<<"st="<<st<<" ed="<<ed<<endl; edge[++num].ed=ed; edge[num].nex=first[st]; first[st]=num; } void addc(int st,int ed){ // cout<<"Stc="<<st<<" edc="<<ed<<endl; edgec[++numc].ed=ed; edgec[numc].nex=firstc[st]; firstc[st]=numc; } void tarjan(int x){ dfn[x]=low[x]=++ord; sta[++top]=x;ins[x]=1; for(int i=first[x];i;i=edge[i].nex){ int y=edge[i].ed; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); }else if(ins[y]) low[x]=min(low[x],dfn[y]); }if(dfn[x]==low[x]){ sccnum++;int p; do{ p=sta[top--];ins[p]=0; bl[p]=sccnum;scc[sccnum].push_back(p); }while(p!=x); } } void topsort(){ queue<int>q; for(int i=1;i<=sccnum;i++) if(du[i]==0) q.push(i); while(!q.empty()){ int x=q.front();q.pop(); // cout<<x<<" "; for(int i=firstc[x];i;i=edgec[i].nex){ int y=edgec[i].ed; du[y]--; s[y]|=s[x]; if(du[y]==0) q.push(y); } } } int main(){ // freopen("a,in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",ch+1); for(int j=1;j<=n;j++) if(ch[j]-'0') add(i,j); } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;i++) for(int j=first[i];j;j=edge[j].nex){ int y=edge[j].ed; if(bl[i]==bl[y]) continue; addc(bl[i],bl[y]); du[bl[y]]++; } for(int i=1;i<=sccnum;i++){ for(int j=0;j<scc[i].size();j++){ int y=scc[i][j]; s[i][y]=1; } } topsort(); for(int i=1;i<=sccnum;i++) ans+=s[i].count()*scc[i].size(); printf("%d ",ans); return 0; }