这个题目有毒。
我同学跟我说这个是强连通分量水题,然后我就打 ,结果
思考许久没想出来哪里错了。
于是仔细思考,可以用BFS做,于是我很愉快的打了一个 ,AC啦!
原来 一道 省选/NOI- 的JS省选紫题就这么水过了
愉快的打了个 普及/提高-
回家仔细思考哪里错了。
发现自己有不少写错了
1)建边的时候 把 写成了
2)强连通分量 没用 ins 还有 还有 把 写成了
3)new_E 把 所有的 写成了 (看来够昏头的,机房效率不高啊)
4)solve 里 把所有的 写成了 (正好写反了!!!)
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) ;
}
强连通的做法就要稍微难想一点。
嗯,首先这个题目给了我们一个定义:连通数:指途中可达点对的个数。
其实首先这个定义我就并没有十分看懂,
其实这个东西的意思非常简单,就是针对每一个点,
我们计算这个点所能够到达的点的数量之和,
(算上自身)然后将所有点的这个数量加起来就是连通数了。
可以通过缩点优化
缩完点之后,我们重新构图。
记录 表示i(缩完点后的新点)所表示的点包含旧图中点的个数
时间复杂度
表示 能到达的点集 (用bitset优化)
构完图之后,把入度为0的点加入队列。
之后像类似拓扑排序那样当入度为0就加入队列
然后对于每一次 即将更新的 用这个更新
然后统计一下答案就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
如果
然后统计一下也可以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);
}