2208: [Jsoi2010]连通数
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2929 Solved: 1280
[ Submit][ Status][ Discuss]
Description
Input
输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。
Output
输出一行一个整数,表示该图的连通数。
Sample Input
3
010
001
100
Sample Output
9
求一波强连通分量,预处理所有的scc[],其中所有在同一强连通分量里的点scc值相同
然后缩点形成一张有向无环图(可拓扑图)
很显然每个scc自身对答案的贡献就是siz(scc)²
不同scc之间的贡献在拓扑图上总体进行dp就好了
注意这里不用再求拓扑序,因为Trajan之后scc本身就已经有序了!
假设x点的scc值高,y点的scc值低,那么x点和y点要不不连通,要不只能从x到y!
用bieset会方便的多
out[a][b]表示第a个scc与第b节点的联通关系
#include<stdio.h>
#include<vector>
#include<bitset>
#include<stack>
#include<algorithm>
using namespace std;
vector<int> G[2005];
stack<int> st;
bitset<2005> out[2005], temp;
int t, cnt, scc[2005], low[2005], Time[2005], sum[2005];
void Trajan(int u)
{
int i, v;
st.push(u);
low[u] = Time[u] = ++t;
for(i=0;i<G[u].size();i++)
{
v = G[u][i];
if(Time[v]==0)
{
Trajan(v);
low[u] = min(low[v], low[u]);
}
else if(scc[v]==0)
low[u] = min(low[u], Time[v]);
}
if(low[u]==Time[u])
{
++cnt;
while(st.empty()==0)
{
v = st.top();
st.pop();
scc[v] = cnt;
sum[cnt]++;
if(v==u)
break;
}
}
}
int main(void)
{
int n, i, j, x, k, ans, v;
scanf("%d", &n);
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
scanf("%1d", &x);
if(x==1)
G[i].push_back(j);
}
}
for(i=1;i<=n;i++)
{
if(Time[i]==0)
Trajan(i);
}
for(i=1;i<=n;i++)
out[scc[i]][i] = 1;
ans = 0;
for(i=1;i<=cnt;i++)
{
temp.reset();
for(j=1;j<=n;j++)
{
if(scc[j]!=i)
continue;
for(k=0;k<G[j].size();k++)
{
v = G[j][k];
if(scc[v]<i)
temp |= out[scc[v]];
}
}
out[i] |= temp;
ans += sum[i]*out[i].count();
}
printf("%d\n", ans);
return 0;
}