P4306 [JSOI2010]連通數
阿新 • • 發佈:2018-11-01
https://www.luogu.org/problemnew/show/P4306
Problem:給你一張個點條邊的有向圖,現定義點對表示連通關係:從點能直接或間接到達點,問你圖中存在多少對這樣的關係。
關於原題的題意,需注意幾點:
1.因為題目給的是有向圖,所以(存在從點到達點的路徑,下同)並不一定代表(存在從點到達點的路徑)。
2.也算是一個合法的點對。
明確這兩點後我們就可以考慮怎麼做了。
因為涉及點與點之間之間的連通關係,我首先往tarjan上想的(雖然聽大佬說他們都是用Floyd傳遞閉包做的emmm)。將原圖縮完點之後,我們可知:對於點,它能到達的點只會是兩種:
1.和它同在一個強聯通分量的點;
2.點所在的強聯通分量所能到達的其它強聯通分量裡的所有點。
所以,我們以每一個強聯通分量為單位統計答案。
設強連通分量內的點的個數為,那麼強連通分量對該題答案的貢獻就是:。
AC Code:
#include<cstdio> #include<cstring> #include<iostream> #define ri register int using namespace std; const int MAXN=2020; int n,m,u[MAXN*MAXN],v[MAXN*MAXN],fst[MAXN*MAXN],nxt[MAXN*MAXN]; int dfn[MAXN],low[MAXN],sta[MAXN],top,cnt,vis[MAXN],ctot,b[MAXN],stot,from[MAXN*MAXN],to[MAXN*MAXN]; int book[MAXN],w[MAXN],dp[MAXN],ans; char ss[MAXN]; void tarjan(int x) { dfn[x]=++cnt,low[x]=dfn[x],sta[++top]=x,vis[x]=1; for(ri k=fst[x];k>0;k=nxt[k]) { if(dfn[v[k]]&&vis[v[k]]) low[x]=min(low[x],dfn[v[k]]); if(!dfn[v[k]]) { tarjan(v[k]); low[x]=min(low[x],low[v[k]]); } } if(low[x]==dfn[x]) { ctot++; while(1) { vis[sta[top]]=0,b[sta[top]]=ctot,w[ctot]++; top--; if(sta[top+1]==x) break; } } } void dfs(int x,int num) { book[x]=1; for(ri k=fst[x];k>0;k=nxt[k]) if(!book[to[k]]) { dp[num]+=w[to[k]]; dfs(to[k],num); } } int main() { scanf("%d",&n); for(ri i=1;i<=n;i++) { scanf("%s",ss+1); for(ri j=1;j<=n;j++) { if(ss[j]=='1') { ++m; u[m]=i,v[m]=j; nxt[m]=fst[u[m]],fst[u[m]]=m; } } } for(ri i=1;i<=n;i++) if(!dfn[i]) tarjan(i); memset(fst,0,sizeof(fst)); memset(nxt,0,sizeof(nxt)); for(ri i=1;i<=m;i++) { if(b[u[i]]==b[v[i]]) continue; stot++; from[stot]=b[u[i]],to[stot]=b[v[i]]; nxt[stot]=fst[from[stot]],fst[from[stot]]=stot; } n=ctot; for(ri i=1;i<=n;i++)//O(n^2)算每一個SCC對答案的貢獻 { memset(book,0,sizeof(book)); dfs(i,i); dp[i]*=w[i]; } for(ri i=1;i<=ctot;i++) ans+=w[i]*w[i]+dp[i]; cout<<ans; return 0; }