题目大意:给出一张有向图,若一个点能够到达另一个点,那么说这两个点是一对联通点。问图中共有多少联通点。
思路:先进行一次Tarjan,求出所有的scc,对于一个scc中的点,对答案的贡献就是cnt^2,不同的scc组成了一张可拓扑图,然后对于每个scc维护一个bitset,来统计他自己和标号比它小的scc中共有多少个不同的点。然后进行dp,其中不停的或就可以了。
第一次使用bitset
CODE:
#include <bitset>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAXP 2010
#define MAX 4000010
using namespace std;
int points;
int head[MAXP],total;
int next[MAX],aim[MAX];
inline void Add(int x,int y)
{
next[++total] = head[x];
aim[total] = y;
head[x] = total;
}
int dfn[MAXP],low[MAXP],_clock;
int stack[MAXP],top;
bool in_stack[MAXP];
int changed[MAXP],scc,num[MAXP];
bitset<MAXP> have[MAXP];
void Tarjan(int x)
{
dfn[x] = low[x] = ++_clock;
stack[++top] = x;
in_stack[x] = true;
for(int i = head[x]; i; i = next[i]) {
if(!dfn[aim[i]])
Tarjan(aim[i]),low[x] = min(low[x],low[aim[i]]);
else if(in_stack[aim[i]])
low[x] = min(low[x],dfn[aim[i]]);
}
if(dfn[x] == low[x]) {
int temp;
++scc;
do {
temp = stack[top--];
in_stack[temp] = false;
changed[temp] = scc;
++num[scc];
}while(temp != x);
}
}
int main()
{
cin >> points;
for(int i = 1; i <= points; ++i)
for(int x,j = 1; j <= points; ++j) {
scanf("%1d",&x);
if(x) Add(i,j);
}
for(int i = 1; i <= points; ++i)
if(!dfn[i]) Tarjan(i);
for(int i = 1; i <= points; ++i)
have[changed[i]][i] = true;
int ans = 0;
for(int i = 1; i <= scc; ++i) {
ans += num[i] * num[i];
bitset<MAXP> temp;
for(int x = 1; x <= points; ++x)
if(changed[x] == i)
for(int j = head[x]; j; j = next[j])
if(changed[aim[j]] != i)
temp |= have[changed[aim[j]]];
ans += num[i] * temp.count();
have[i] |= temp;
}
cout << ans << endl;
return 0;
}BZOJ 2208 JSOI 2010 连通数 Tarjan+bitset
原文地址:http://blog.csdn.net/jiangyuze831/article/details/42102711