等价性问题,给出的样例为 a->b的形式,问要实现全部等价(即任意两个可以互相推出),至少要加多少个形如 a->b的条件。
容易想到用强连通缩点,把已经实现等价的子图缩掉,最后剩余DAG。要推出一个方案,YY后取“出度为零”和“入度为零”的点数的较大值。
理由:假定出度为零的点数较多,即是我们通常意义上的树的形式(当然,DAG是图,这里只是类比)。
根可以推出其所有子孙,事实上任意一个点都可以推出其子孙,那么只要让该节点推出树根,就可以推出整棵树上所有的节点了。那么多棵树为什么不是相乘呢?,借题目中的范例,a->b,b->c,c->a,形成一个循环即可。
本质:给定一个有向图,问是否加上多少条边,使原图构成强连通。
1 #include<stdio.h> 2 #include<string.h> 3 #include<stack> 4 #include<algorithm> 5 using namespace std; 6 7 const int MAXN=55555; 8 9 struct Edge{ 10 int v,next; 11 Edge(){} 12 Edge(int _v,int _next):v(_v),next(_next){} 13 }edge[MAXN]; 14 15 int tol,head[MAXN]; 16 int low[MAXN],dfn[MAXN],sccno[MAXN],scc_cnt,TT; 17 18 stack<int >stk; 19 20 void init() 21 { 22 tol=0; 23 memset(head,-1,sizeof(head)); 24 } 25 26 void add(int u,int v) 27 { 28 edge[tol]=Edge(v,head[u]); 29 head[u]=tol++; 30 } 31 32 void dfs(int u) 33 { 34 int v; 35 dfn[u]=low[u]=++TT; 36 stk.push(u); 37 for(int i=head[u];i!=-1;i=edge[i].next) 38 { 39 v=edge[i].v; 40 if(!dfn[v]){ 41 dfs(v); 42 low[u]=min(low[u],low[v]); 43 }else if(!sccno[v]){ 44 low[u]=min(low[u],dfn[v]); 45 } 46 } 47 if(low[u]==dfn[u]){ 48 scc_cnt++; 49 do{ 50 v=stk.top(); 51 stk.pop(); 52 sccno[v]=scc_cnt; 53 }while(v!=u); 54 } 55 } 56 57 void tarjan(int n) 58 { 59 scc_cnt=TT=0; 60 memset(low,0,sizeof(low)); 61 memset(dfn,0,sizeof(dfn)); 62 memset(sccno,0,sizeof(sccno)); 63 64 while(!stk.empty()) 65 stk.pop(); 66 for(int i=1;i<=n;i++) 67 if(!dfn[i]) 68 dfs(i); 69 } 70 71 int main() 72 { 73 int T,n,m; 74 int a[MAXN],b[MAXN]; 75 scanf("%d",&T); 76 while(T--) 77 { 78 scanf("%d%d",&n,&m); 79 80 init(); 81 for(int i=0;i<m;i++) 82 { 83 scanf("%d%d",&a[i],&b[i]); 84 add(a[i],b[i]); 85 } 86 87 tarjan(n); 88 89 int in[MAXN],out[MAXN]; 90 memset(in,0,sizeof(in)); 91 memset(out,0,sizeof(out)); 92 for(int i=0;i<m;i++) 93 { 94 if(sccno[a[i]]!=sccno[b[i]]){ 95 in[sccno[b[i]]]++; 96 out[sccno[a[i]]]++; 97 } 98 } 99 100 int innum,outnum; 101 innum=outnum=0; 102 for(int i=1;i<=scc_cnt;i++) 103 { 104 if(!in[i]) 105 innum++; 106 if(!out[i]) 107 outnum++; 108 } 109 110 if(scc_cnt==1)printf("0 "); 111 else printf("%d ",max(innum,outnum)); 112 } 113 return 0; 114 }