[JSOI2010]连通数

版权声明:版权所有,转载请标明博文出处。 https://blog.csdn.net/HQG_AC/article/details/82692192

这个题目有毒。

我同学跟我说这个是强连通分量水题,然后我就打 t a r j a n ,结果 W A W A W A

思考许久没想出来哪里错了。

于是仔细思考,可以用BFS做,于是我很愉快的打了一个 B F S ,AC啦!

原来 一道 省选/NOI- 的JS省选紫题就这么水过了

愉快的打了个 普及/提高-

回家仔细思考哪里错了。

发现自己有不少写错了

1)建边的时候 把 S [ j ] == 1 写成了 0

2)强连通分量 没用 ins 还有 还有 把 t a r j a n ( t o ) 写成了 t a r j a n ( i )

3)new_E 把 所有的 e [ i ] 写成了 G [ i ] (看来够昏头的,机房效率不高啊)

4)solve 里 把所有的 G [ x ] 写成了 e [ x ] (正好写反了!!!)

5)没开long long

好了,不说自己的经历了,讲正解怎么做的

首先,BFS真的很好想,对,就是直接对于每个点遍历其路径即可,然后+n即可(i和i肯定是连通的么)。


// Author : harry
// Language : C++ (GNU C++ 11)
// Upload : luogu
// Time : 2018.9.13 
// Problem : 连通数 【bfs版】 
// Tell Myself : Think Twice Code Once
// All rights reserved

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <cctype>
#include <string>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std ;

#define rep(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define Rep(i,a,b) for (int (i)=(a)-1;(i)<(b);(i)++)
#define REP(i,a,b) for (int (i)=(a);(i)>=(b);(i)--)
#define reg(i,x) for (int (i)=head[x];(i);i=e[i].next)
#define clear(a) memset(a,0,sizeof(a))

#define ull unsigned long long
#define ll long long
#define ls ((x)<<1)
#define rs ((x)<<1|1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define endl '\n'
#define Pii pair<int,int>

const int N = 2010 ;
const int iinf = INT_MAX/2 ;
const ll linf = LONG_MAX/2 ;
const int MOD = 1e9+7 ;

inline int read(){
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}

void write(int x){
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

int vis[N] ;
vector<int> e[N] ;
ll ans=0;
int n ;

void bfs(int rt){
    queue <int> q ;
    memset(vis,0,sizeof(vis)) ;
    q.push(rt) ;
    vis[rt]=1 ;
    while(!q.empty()){
        int x=q.front();q.pop() ;
        for (int i=0;i<e[x].size();i++)
        if (!vis[e[x][i]]){
            ans++ ;
            vis[e[x][i]]=1 ;
            q.push(e[x][i]) ;
        }
    }
}

string S ;

int main(){
    scanf("%d",&n) ;
    for (int i=1;i<=n;i++){
        cin>>S ;
        for (int j=0;j<n;j++) if (S[j]=='1') e[i].pb(j+1) ;
    }

    for (int i=1;i<=n;i++) bfs(i) ;
    printf("%lld\n",ans+n) ;
}

强连通的做法就要稍微难想一点。

嗯,首先这个题目给了我们一个定义:连通数:指途中可达点对的个数。

其实首先这个定义我就并没有十分看懂,

其实这个东西的意思非常简单,就是针对每一个点,

我们计算这个点所能够到达的点的数量之和,

(算上自身)然后将所有点的这个数量加起来就是连通数了。

可以通过缩点优化

缩完点之后,我们重新构图。

记录 s u m [ i ] 表示i(缩完点后的新点)所表示的点包含旧图中点的个数

时间复杂度 O ( n 3 )

f [ i ] 表示 i 能到达的点集 s (用bitset优化)

构完图之后,把入度为0的点加入队列。

之后像类似拓扑排序那样当入度为0就加入队列

然后对于每一次 即将更新的 用这个更新 f [ G [ x ] [ i ] ] | = f [ x ]

然后统计一下答案就AC了

回顾之后感觉还是挺水的


// Author : harry
// Language : C++ (GNU C++ 11)
// Upload : luogu 
// Time : 2018.9.13
// Problem : 连通数 【强连通版】 
// Tell Myself : Think Twice Code Once
// All rights reserved

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <string>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std ;

#define rep(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define Rep(i,a,b) for (int (i)=(a)-1;(i)<(b);(i)++)
#define REP(i,a,b) for (int (i)=(a);(i)>=(b);(i)--)
#define reg(i,x) for (int (i)=head[x];(i);i=e[i].next)
#define clear(a) memset(a,0,sizeof(a))

#define ull unsigned long long
#define ll long long
#define ls ((x)<<1)
#define rs ((x)<<1|1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define endl '\n'
#define Pii pair<int,int>

const int N = 2010 ;
const int iinf = INT_MAX/2 ;
const ll linf = LONG_MAX/2 ;
const int MOD = 1e9+7 ;

inline int read(){
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}

void write(int x){
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

vector<int> e[N],G[N] ;

int low[N],dfn[N],ins[N],col[N],a[N],ind[N] ;
stack <int> s ;

bitset <N> f[N] ;
int n,sum,t;
ll ans ;

void tarjan(int rt){
    dfn[rt]=low[rt]=++t ;
    ins[rt]=1;s.push(rt) ;

    for (int i=0;i<e[rt].size();i++){
        int to=e[rt][i] ;
        if (!dfn[to]){
            tarjan(to) ;
            low[rt]=min(low[rt],low[to]) ;
        }
        else if (ins[to]) low[rt]=min(low[rt],dfn[to]) ;
    }

    if (dfn[rt]==low[rt]){ //缩点 
        col[rt]=++sum ;
        ins[rt]=-1 ;
        a[sum]=1 ;
        while(s.top()!=rt){
            col[s.top()]=sum ;
            ins[s.top()]=-1 ;
            a[sum]++ ;
            s.pop() ;
        }
        s.pop() ;
    }
}

void new_E() { //重新构图 
    for (int i=1;i<=n;i++)
    for (int j=0;j<e[i].size();j++)
    if (col[i]!=col[e[i][j]]){
        G[col[i]].pb(col[e[i][j]]) ;
        ind[col[e[i][j]]]++ ;
    }
}

void solve(){
    queue <int> q ;
    while (!q.empty()) q.pop() ;
    for (int i=1;i<=sum;i++) f[i][i]=1 ;
    for (int i=1;i<=sum;i++) if (!ind[i]) q.push(i) ; 
    while(!q.empty()){
        int x=q.front();q.pop() ;
        for (int i=0;i<G[x].size();i++){
            f[G[x][i]]|=f[x] ;
            ind[G[x][i]]-- ;
            if (!ind[G[x][i]]) q.push(G[x][i]) ;
        }
    }
}

string S ;

int main(){
    scanf("%d",&n) ;
    for (int i=1;i<=n;i++){
        cin>>S ;
        for (int j=0;j<n;j++) if (S[j]=='1') e[i].pb(j+1) ;
    }

    for (int i=1;i<=n;i++) if (!ins[i]) tarjan(i) ;

    new_E() ;
    solve() ;

    for (int i=1;i<=sum;i++)
    for (int j=1;j<=sum;j++)
    if (f[i][j]) ans+=a[i]*a[j] ;
    printf("%lld\n",ans) ;
}

受f[i]的启发,感觉floyd也能做这题

也是bitset

如果 f [ j ] [ i ] f [ j ] | = f [ i ]

然后统计一下也可以AC


// Author : harry
// Language : C++ (GNU C++ 11)
// Upload : luogu 
// Time : 2018.9.13
// Problem : [JSOI2010]连通数 【floyd 版】 
// Tell Myself : Think Twice Code Once
// All rights reserved

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <vector>
#include <bitset>
#include <cstdio>
#include <cctype>
#include <string>
#include <cstring>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std ;

#define rep(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define Rep(i,a,b) for (int (i)=(a)-1;(i)<(b);(i)++)
#define REP(i,a,b) for (int (i)=(a);(i)>=(b);(i)--)
#define reg(i,x) for (int (i)=head[x];(i);i=e[i].next)
#define clear(a) memset(a,0,sizeof(a))

#define ull unsigned long long
#define ll long long
#define ls ((x)<<1)
#define rs ((x)<<1|1)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define endl '\n'
#define Pii pair<int,int>

const int N = 2010 ;
const int iinf = INT_MAX/2 ;
const ll linf = LONG_MAX/2 ;
const int MOD = 1e9+7 ;

inline int read(){
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}

void write(int x){
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}

string s ;
bitset <N> f[N];
int n ;
ll ans ;

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        cin>>s ;
        for (int j=0;j<n;j++) if(s[j]=='1') f[i][j+1]=1;
        f[i][i]=1;
    }

    for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    if (f[j][i]) f[j]|=f[i];

    for(int i=1;i<=n;i++) ans+=f[i].count(); 
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/HQG_AC/article/details/82692192