tarjan缩点+拓扑
这道题我对拍了4K组,发现是数组开小了,CNBB
一开始有一个在lg上能A在BZ上WA掉的想法(后来自己造的数据卡掉了),就是我们缩完点后,反向连边,假如a->b,那就连一条b->a的边,表示a能到b,那么拓扑的时候就可以直接将b的siz加到a上,然后自己造的数据是
4
0010
0010
1000
0110
然后发现这个操作不行,就换一种写法反向连边,然后假如新图中能到a的集合为E,且a->b,那么E肯定定能到A,然后就拓扑的时候,用a更新b就行了,最后统计答案,a能到b,那么b对a中每个点的贡献都是siz[b],那么对a的总贡献就是siz[a]*siz[b],a对其内部的贡献是siz[a]*siz[a]因为强联通分量内部互相可达
代码(方法一)
//By AcerMo
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=1000500;
int n,m;
bool jud[2050][2050];
char map[2050][2050];
int cnt,to[M],nxt[M],head[M],in[M];
int low[M],dfn[M],vis[M],fa[M],siz[M],ind,ti;
queue<int>q;
stack<int>s;
vector<int>v[M];
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y)
{
to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
return ;
}
inline void tarjan(int x)
{
dfn[x]=low[x]=++ind;vis[x]=1;s.push(x);
for (int i=head[x];i;i=nxt[i])
{
if (!dfn[to[i]])
tarjan(to[i]),low[x]=min(low[x],low[to[i]]);
else if (vis[to[i]])
low[x]=min(low[x],dfn[to[i]]);
}
if (low[x]==dfn[x])
{
ti++;int u=-1;
while (u!=x)
{
u=s.top();s.pop();
vis[u]=0;fa[u]=ti;
siz[ti]++;
}
}
return ;
}
inline void built()
{
for (int i=1;i<=n;i++)
for (int k=head[i];k;k=nxt[k])
if (fa[i]!=fa[to[k]]&&!jud[fa[to[k]]][fa[i]])
v[fa[to[k]]].push_back(fa[i]),in[fa[i]]++,jud[fa[to[k]]][fa[i]]=1;
return ;
}
inline void topsort()
{
for (int i=1;i<=ti;i++) if (!in[i]) q.push(i);
while (q.size())
{
int u=q.front();q.pop();
for (int i=0;i<v[u].size();i++)
{
int go=v[u][i];
in[go]--;siz[go]+=siz[u];
if (!in[go]) q.push(go);
}
}
return ;
}
signed main()
{
n=read();
for (int i=1;i<=n;i++) scanf("%s",map[i]+1);
for (int i=1;i<=n;i++)
for (int k=1;k<=n;k++)
if (map[i][k]=='1') add(i,k);
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
built();topsort();int ans=0;
for (int i=1;i<=n;i++) ans+=siz[fa[i]];
cout<<ans;
return 0;
}
代码(方法二)
//By AcerMo
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=4000500;
const int N=2050;
int n,m;
char map[N][N];
bool ono[N][N];
int cnt,to[M],nxt[M],head[M],in[M];
int low[N],dfn[N],vis[N],fa[N],siz[N],ind,ti;
queue<int>q;
stack<int>s;
vector<int>v[M];
inline int read()
{
int x=0;char ch=getchar();
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void add(int x,int y)
{
to[++cnt]=y;nxt[cnt]=head[x];head[x]=cnt;
return ;
}
inline void tarjan(int x)
{
dfn[x]=low[x]=++ind;vis[x]=1;s.push(x);
for (int i=head[x];i;i=nxt[i])
{
if (!dfn[to[i]])
tarjan(to[i]),low[x]=min(low[x],low[to[i]]);
else if (vis[to[i]])
low[x]=min(low[x],dfn[to[i]]);
}
if (low[x]==dfn[x])
{
ti++;int u=-1;
while (u!=x)
{
u=s.top();s.pop();
vis[u]=0;fa[u]=ti;
siz[ti]++;
}
}
return ;
}
inline void built()
{
for (int i=1;i<=n;i++)
for (int k=head[i];k;k=nxt[k])
if (fa[i]!=fa[to[k]])
v[fa[to[k]]].push_back(fa[i]),in[fa[i]]++,ono[fa[i]][fa[to[k]]]=1;
return ;
}
inline void topsort()
{
for (int i=1;i<=ti;i++) if (!in[i]) q.push(i);
while (q.size())
{
int u=q.front();q.pop();
for (int i=0;i<v[u].size();i++)
{
int go=v[u][i];
in[go]--;ono[go][u]=1;
for (int i=1;i<=ti;i++)
if (ono[u][i]) ono[go][i]=1;
if (!in[go]) q.push(go);
}
}
return ;
}
signed main()
{
n=read();
for (int i=1;i<=n;i++) scanf("%s",map[i]+1);
for (int i=1;i<=n;i++)
for (int k=1;k<=n;k++)
if (map[i][k]=='1') add(i,k);
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
built();topsort();int ans=0;
for (int i=1;i<=ti;i++)
for (int k=1;k<=ti;k++)
if (ono[i][k]||i==k) ans+=(siz[k]*siz[i]);
cout<<ans;
return 0;
}