tarjan求关键连接java,【BZOJ2208】[JSOI2010]连通数(Tarjan)

博主分享了一道关于有向图连通数的题目,原始解法时间复杂度为$O(nm)$。通过观察问题本质,利用拓扑排序和bitset,博主提出了一个优化后的$O(n+m)$解决方案。文章详细介绍了如何将有向图简化为DAG并使用dp或bitset进行高效计算。
摘要由CSDN通过智能技术生成

【BZOJ2208】[JSOI2010]连通数(Tarjan)

题面

题解

先吐槽辣鸡洛谷数据,我写了个$O(nm)$的都过了。

#include

#include

using namespace std;

#define MAX 2020

struct Line{int v,next;}e[MAX*MAX];

int h[MAX],cnt=1;

inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}

int vis[MAX],ans,dep,n;char g[MAX];

void dfs(int u)

{

if(vis[u]==dep)return;

vis[u]=dep;++ans;

for(int i=h[u];i;i=e[i].next)dfs(e[i].v);

}

int main()

{

scanf("%d",&n);

for(int i=1;i<=n;++i)

{

scanf("%s",g+1);

for(int j=1;j<=n;++j)

if(g[j]=='1')Add(i,j);

}

for(int i=1;i<=n;++i)dep=i,dfs(i);

printf("%d\n",ans);

}

正经点。 这玩意既然是有向图,直接给他缩了就变$DAG$了,似乎直接$dp$一下就好了? 然而直接$dp$没法维护哪些点走过了,所以咱来$bitset$一下就好了?

#include

#include

#include

#include

#include

using namespace std;

#define MAX 2020

struct Line{int v,next;}e[MAX*MAX];

int h[MAX],cnt=1,dg[MAX];

inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}

int n;char g[MAX];

bool ins[MAX];

int dfn[MAX],low[MAX],St[MAX],top,gr,G[MAX],tim,sz[MAX];

bitset<2020> s[MAX];

vector E[MAX];

void Tarjan(int u)

{

dfn[u]=low[u]=++tim;ins[u]=true;St[++top]=u;

for(int i=h[u];i;i=e[i].next)

{

int v=e[i].v;

if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);

else if(ins[v])low[u]=min(low[u],dfn[v]);

}

if(dfn[u]==low[u])

{

int v;++gr;

do{v=St[top--];ins[v]=false;G[v]=gr;s[gr].set(v);++sz[gr];}while(u!=v);

}

}

int p[MAX],tot,ans;

void Topsort()

{

queue Q;

for(int i=1;i<=gr;++i)if(!dg[i])Q.push(i);

while(!Q.empty())

{

int u=Q.front();p[++tot]=u;Q.pop();

for(int i=0,l=E[u].size();i

if(!--dg[E[u][i]])Q.push(E[u][i]);

}

for(int i=tot;i;--i)

for(int j=0,l=E[p[i]].size();j

s[p[i]]|=s[E[p[i]][j]];

}

int main()

{

scanf("%d",&n);

for(int i=1;i<=n;++i)

{

scanf("%s",g+1);

for(int j=1;j<=n;++j)

if(g[j]=='1')Add(i,j);

}

for(int i=1;i<=n;++i)if(!dfn[i])Tarjan(i);

for(int u=1;u<=n;++u)

for(int i=h[u];i;i=e[i].next)

if(G[u]!=G[e[i].v])

E[G[u]].push_back(G[e[i].v]),++dg[G[e[i].v]];

Topsort();

for(int i=1;i<=gr;++i)ans+=s[i].count()*sz[i];

printf("%d\n",ans);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值