卧薪尝胆,厚积薄发。
JSOI2010 连通数
Date: Mon Sep 24 08:14:36 CST 2018 In Category: NoCategory

Description:

求有向图中可达的点对个数。
$1\le n \le 2000$

Solution:

先跑 $\text{tarjan}$ 缩个点,然后用 $\text{bitset}$ 暴力标记,反向拓扑。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<stack>
#include<queue>
#include<bitset>
#include<vector>
#include<cstring>
using namespace std;
int getbit()
{
register char c = getchar();
while(!isdigit(c))c = getchar();
return c - '0';
}
int n;
#define MAXN 2010
struct edge
{
int to,nxt;
}e[MAXN * MAXN];
int edgenum = 0;
int lin[MAXN] = {0};
void add(int a,int b)
{
++edgenum;e[edgenum].to = b;e[edgenum].nxt = lin[a];lin[a] = edgenum;
return;
}
stack<int> s;
int dfn[MAXN],low[MAXN],tot = 0;
bool v[MAXN];
int scc = 0,ins[MAXN],siz[MAXN];
bitset<MAXN> f[MAXN];
void tarjan(int k)
{
dfn[k] = low[k] = ++tot;
s.push(k);v[k] = true;
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(dfn[e[i].to] == 0)
{
tarjan(e[i].to);
low[k] = min(low[k],low[e[i].to]);
}
else if(v[e[i].to])
{
low[k] = min(low[k],dfn[e[i].to]);
}
}
if(dfn[k] == low[k])
{
int t;
++scc;siz[scc] = 0;
do
{
t = s.top();s.pop();
ins[t] = scc;v[t] = false;
++siz[scc];f[scc][t] = true;
}while(t != k);
}
return;
}
vector<int> g[MAXN];
int ind[MAXN];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
if(getbit())add(i,j);
for(int i = 1;i <= n;++i)
if(dfn[i] == 0)tarjan(i);
for(int k = 1;k <= n;++k)
{
for(int i = lin[k];i != 0;i = e[i].nxt)
{
if(ins[k] != ins[e[i].to])
{
g[ins[e[i].to]].push_back(ins[k]);
++ind[ins[k]];
}
}
}
int ans = 0;
queue<int> q;
for(int i = 1;i <= scc;++i)
{
q.push(i);
}
while(!q.empty())
{
int k = q.front();q.pop();
for(vector<int>::iterator i = g[k].begin();i != g[k].end();++i)
{
f[*i] |= f[k];
--ind[*i];
if(ind[*i] == 0)
{
q.push(*i);
}
}
}
for(int i = 1;i <= scc;++i)
{
ans += siz[i] * (f[i].count() - siz[i]) + siz[i] * siz[i];
}
cout << ans << endl;
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡