[Jsoi2010]连通数

本文详细介绍了JSOI2010竞赛题目“连通数”的求解过程,通过Tarjan算法进行缩点处理,并利用拓扑排序和状态压缩技术计算图中各个节点间的可达性,最终得出所有可能的连通块数量。

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

[Jsoi2010]连通数

先Tarjan缩点,然后建反图拓扑看每一个点可以由哪里经过,用到bitset定义zt状态压缩一下,拓扑图中x->y :则表示y可以到达x,所以zt[y]=zt[y]|zt[x]就知道了y的,然后在for一下每个点及其所到达的点, Cgema算出两两乘积得出ans。

 

  1 #define MAXN 2010UL
  2 
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<queue>
  6 #include<iostream>
  7 #define MIN(a,b) (a)<(b)?(a):(b);
  8 using namespace std;
  9 
 10 int n,head[MAXN],cnt,id,sum;
 11 char key;
 12 int dfn[MAXN],low[MAXN],bel[MAXN],front[MAXN],ans;
 13 int sta[MAXN],tail;
 14 bool vis[MAXN];
 15 bool exi[MAXN][MAXN];
 16 int indu[MAXN];
 17 int be[MAXN];
 18 int ge[MAXN];
 19 queue<int>st;
 20 struct Node{
 21     int en,to;
 22 }eda[4000005];
 23 struct A{
 24     int en,to;
 25 }sg[2000005];
 26 int op=0;
 27 void Add(int x,int y){
 28     eda[++cnt].to=head[x];
 29     eda[cnt].en=y;
 30     head[x]=cnt;
 31 }
 32 void Add2(int x,int y){
 33     sg[++cnt].to=front[x];
 34     sg[cnt].en=y;
 35     front[x]=cnt;
 36 }
 37 int Tarjan(int x){
 38     dfn[x]=++id;
 39     low[x]=id;
 40     vis[x]=1;
 41     sta[++tail]=x;
 42     for(int i=head[x];i;i=eda[i].to){
 43         int y=eda[i].en;
 44         if(!dfn[y]){
 45             Tarjan(y);
 46             low[x]=MIN(low[x],low[y]);
 47         }else if(vis[y]){
 48             low[x]=MIN(dfn[y],low[x]);
 49         }
 50     }
 51     if(dfn[x]==low[x]){
 52         sum++;
 53         while(sta[tail]!=x&&tail){
 54             bel[sta[tail]]=sum;
 55             vis[sta[tail]]=0;
 56             tail--;
 57         }
 58         bel[sta[tail]]=sum;
 59         vis[sta[tail]]=0;
 60         tail--;
 61     }    
 62 }
 63 int dfs(int x){
 64 //    printf("%d\n",x);
 65     op+=ge[x];
 66     vis[x]=1;
 67     for(int i=front[x];i;i=sg[i].to){
 68         if(!vis[sg[i].en])
 69             dfs(sg[i].en);
 70     }
 71 }
 72 int main(){
 73     freopen("connect.in","r",stdin);
 74     freopen("connect.out","w",stdout);
 75     scanf("%d",&n);
 76     for(int i=1;i<=n;i++){
 77         for(int j=1;j<=n;j++){
 78             cin>>key;
 79             if(key=='1')    Add(i,j);
 80         }
 81     }
 82     for(int i=1;i<=n;i++){
 83         if(!dfn[i]) Tarjan(i);
 84     }    
 85     for(int i=1;i<=n;i++){
 86         ge[bel[i]]++;
 87         be[bel[i]]++;
 88         printf("%d belong to %d\n",i,bel[i]);
 89     }
 90     cnt=0;
 91     for(int i=1;i<=n;i++){
 92         for(int j=head[i];j;j=eda[j].to){
 93             int y=bel[eda[j].en];
 94     //        printf("%d -> %d\n",i,eda[j].en);
 95             if(!exi[bel[i]][y]&&y!=bel[i]){
 96                 Add2(y,bel[i]);
 97                 indu[bel[i]]++;
 98                 exi[bel[i]][y]=1;
 99             }
100         }
101     }
102     for(int i=1;i<=sum;i++){
103         printf("%d\n",indu[i]);
104         if(indu[i]==0) st.push(i);
105     } 
106     while(!st.empty()){
107         int u=st.front();
108         ans+=be[u]*ge[u];
109         for(int j=front[u];j;j=sg[j].to){
110             int y=sg[j].en;
111             indu[y]--;
112             ge[y]+=ge[u];
113              if(!indu[y]) st.push(y);
114         }
115         st.pop();
116     }
117     printf("%d",ans);
118 }
119 /*
120 5
121 01100
122 00101
123 00011
124 00000
125 00000
126 */
View Code

 

posted @ 2015-10-11 18:30 Lenicodes 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值