bzoj2208 jsoi2010 连通数

http://www.elijahqi.win/archives/964
Description

Input

输入数据第一行是图顶点的数量,一个正整数N。 接下来N行,每行N个字符。第i行第j列的1表示顶点i到j有边,0则表示无边。

Output

输出一行一个整数,表示该图的连通数。

Sample Input

3
010
001
100
Sample Output

9
HINT

对于100%的数据,N不超过2000。

有点像此前做过的一题floyd 第一次用bitset 啊

为什么倒序做

因为我们tarjan的时候给的序号是递归给的啊,所以拓扑排序完,序号大的其实是真正图中层数小的

#include<cstdio>
#include<stack>
#include<bitset>
#include<queue>
#define N 2200
using namespace std;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
stack<int> q;queue<int> qq;
bitset<N> f[N];char s1[N];
int dfn[N],low[N],tp[N],map[N][N],map1[N][N],num,ans,n,a1[N],s,b[N],size[N];
bool stackf[N];
inline int min(int x,int y){return x<y?x:y;}
void tarjan(int u){
    dfn[u]=++num;low[u]=num;stackf[u]=true;q.push(u);
    for (int i=1;i<=n;++i){
        if (map[u][i]){
            if (!dfn[i]) tarjan(i),low[u]=min(low[u],low[i]);else if (stackf[i]) low[u]=min(low[u],dfn[i]);
        }
    }
    if (dfn[u]==low[u]){
        ++s;int y;
        do{
            y=q.top();b[y]=s;size[s]++;stackf[y]=false;q.pop();
        }while (y!=u);
    }
}
int main(){
    freopen("bzoj2208.in","r",stdin);
    n=read();
    for (int i=1;i<=n;++i) {
        scanf("%s",s1+1);
        for (int j=1;j<=n;++j) map[i][j]=s1[j]-'0';
    }
    for (int i=1;i<=n;++i) if (!dfn[i]) tarjan(i);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=n;++j) {
            if (map[i][j]&&b[i]!=b[j])
                if (!map1[b[i]][b[j]]) map1[b[i]][b[j]]=1,f[b[i]][b[j]]=1,a1[b[j]]++;
        }
    for (int i=1;i<=s;++i) if (a1[i]==0) qq.push(i);num=0;
    //for (int i=1;i<=s;++i) printf("%d ",a1[b[i]]);
    while (!qq.empty()){
        int x=qq.front();qq.pop();tp[++num]=x;
        for (int i=1;i<=s;++i){
            if (map1[x][i]) {
                --a1[i];
                if (!a1[i]) qq.push(i);
            }
        }
    }
    for (int i=1;i<=s;++i) f[i][i]=1;//printf("%d\n",size[i]);
    for (int i=1;i<=s;++i) printf("%d ",tp[i]);
    for (int i=s;i>=1;--i){
        int x=tp[i];
        for (int j=1;j<=s;++j) if (map1[x][j]) f[x]|=f[j];
    }
    for (int i=1;i<=s;++i)
        for (int j=1;j<=s;++j)
            if (f[i][j]) ans+=size[i]*size[j];
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值