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;
}