[tarjan+bitset]BZOJ 2208——[Jsoi2010]连通数

本文介绍了一道名为“连通数”的JSOI2010竞赛题目的解题思路及代码实现。通过Tarjan算法进行缩点处理,并使用bitset优化传递闭包过程,有效地解决了该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2208: [Jsoi2010]连通数

题目描述

这里写图片描述

解题报告

这题的题目描述非常清晰,看到网上有dalao用bitset优化Floyd就过了。博主用tarjan缩点然后用bitset优化传递闭包就可以。

但是看到很多人缩点完刷拓扑啊,其实这个操作很鸡肋。学过2sat应该有所了解,拓扑序就是tarjan刷出来的scc序(画个图就知道了)。

然后最后刷答案的时候注意一下就可以了,我开了两个数组,一个是或起来之后当前节点的状态,一个是原来的状态,这样的操作使调用bitset的次数变多,所以效率很低(3000ms+)。
希望各位dalao能告诉我更优的方法。

#include<cstdio>
#include<bitset>
#include<algorithm>
using namespace std;
const int maxn=2005,maxm=4000005;
int tot[2],lnk[maxn][2],son[maxm][2],nxt[maxm][2],x[maxn];
int n,top,s[maxn],G,scc[maxn],low[maxn],dfn[maxn],tim,ans;
bool ins[maxn],vis[maxn][maxn];
bitset<maxn> a[maxn],b[maxn];
void add(int x,int y,int id){
    nxt[++tot[id]][id]=lnk[x][id];lnk[x][id]=tot[id];son[tot[id]][id]=y;
}
inline char _read(){
    char ch=getchar();
    while (ch!='0'&&ch!='1') ch=getchar();
    return ch;
}
void tarjan(int x){
    dfn[x]=++tim;low[x]=tim;
    s[++top]=x;ins[x]=1;
    for (int j=lnk[x][0];j;j=nxt[j][0])
    if (!dfn[son[j][0]]){tarjan(son[j][0]);low[x]=min(low[x],low[son[j][0]]);}
    else if (ins[son[j][0]]) low[x]=min(low[x],dfn[son[j][0]]);
    if (dfn[x]==low[x]){
        G++;
        while (s[top]!=x) scc[s[top]]=G,ins[s[top]]=0,a[G][s[top--]]=1;
        scc[x]=G;ins[x]=0;a[G][x]=1;top--;
    }
}
void maker(){
    for (int i=1;i<=n;i++)
    for (int j=lnk[i][0];j;j=nxt[j][0])
    if (scc[i]>scc[son[j][0]]&&!vis[scc[i]][scc[son[j][0]]])
    add(scc[i],scc[son[j][0]],1),vis[scc[i]][scc[son[j][0]]];
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    if (_read()=='1') add(i,j,0);
    for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
    for (int i=1;i<=G;i++) x[i]=a[i].count();
    maker();
    for (int i=1;i<=G;i++) b[i]=a[i];
    for (int i=G;i>=1;i--){
        int y=b[i].count();ans+=x[i]*x[i];
        for (int j=lnk[i][1];j;j=nxt[j][1]){
            ans+=(y-(b[i]&b[son[j][1]]).count())*x[son[j][1]];
            b[son[j][1]]=b[son[j][1]]|b[i];
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值