题目大意:给出一张有向图,若一个点能够到达另一个点,那么说这两个点是一对联通点。问图中共有多少联通点。
思路:先进行一次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