【BZOJ1093】【ZJOI2007】最大半联通子图 [DP][Tarjan]
最大半连通子图
Time Limit: 30 Sec Memory Limit: 162 MB[Submit][Status][Discuss]
Description
一个有向图G=(V,E)称为半连通的(Semi-Connected):
如果满足:∀u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。
若G'=(V',E')满足V'∈V,E'是E中所有跟V'有关的边,则称G'是G的一个导出子图。
若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。
若G'是G所有半连通子图中包含节点数最多的,则称G'是G的最大半连通子图。
给定一个有向图G,请求出G的最大半连通子图拥有的节点数K ,以及不同的最大半连通子图的数目C。
由于C可能比较大,仅要求输出C对X的余数。
Input
第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。
接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
Sample Input
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
1 2
2 1
1 3
2 4
5 6
6 4
Sample Output
3
3
3
HINT
N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8
Main idea
求最大半联通子图大小与个数。(最大半联通子图定义:在这个图内对于任意节点u,v,存在一条u->v的路径)
Solution
先跑一遍Tarjan,得到了两两连通的图,然后考虑如何加入单向连通的点集,显然两个强连通分量之间要是有连边的话,就可以满足这两个强连通分量的点单向连通,符合题意。
那么答案显然就是在缩点后的DAG(有向无环图)上的最长路径。
用拓扑+DP(本质是在拓扑序上的DP)可以求出即为Ans,然后在跑的时候用一个数组f[i]统计一下相同的个数,注意更新dist的时候也要更新f,最后如果dist[i]=Ans,那么累加f[i],即为答案。
Code
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cmath> 8 using namespace std; 9 10 const int ONE=2000001; 11 12 int n,m,MOD; 13 int x,y; 14 int Next[ONE],First[ONE],Go[ONE],tot; 15 int next[ONE],first[ONE],go[ONE],Input[ONE]; 16 int dist[ONE]; 17 int T,t; 18 int tou,wei,jishu; 19 int q[ONE]; 20 int Ans,num,f[ONE]; 21 int Dfn[ONE],Low[ONE],vis[ONE],F[ONE],Num[ONE]; 22 23 struct power 24 { 25 int u,v; 26 }a[ONE]; 27 28 int cmp(const power &a,const power &b) 29 { 30 if(a.u==b.u) return a.v<b.v; 31 return a.u<b.u; 32 } 33 34 int rule(const power &a,const power &b) 35 { 36 return (a.u==b.u && a.v==b.v); 37 } 38 39 int get() 40 { 41 int res,Q=1; char c; 42 while( (c=getchar())<48 || c>57) 43 if(c=='-')Q=-1; 44 if(Q) res=c-48; 45 while((c=getchar())>=48 && c<=57) 46 res=res*10+c-48; 47 return res*Q; 48 } 49 50 int Add(int u,int v) 51 { 52 Next[++tot]=First[u]; First[u]=tot; Go[tot]=v; 53 } 54 55 int Add_edge(int u,int v) 56 { 57 next[++tot]=first[u]; first[u]=tot; go[tot]=v; Input[v]++; 58 } 59 60 void Tarjan(int u) 61 { 62 Dfn[u]=Low[u]=++T; 63 vis[u]=1; 64 q[++t]=u; 65 int v; 66 for(int e=First[u];e;e=Next[e]) 67 { 68 int v=Go[e]; 69 if(!Dfn[v]) 70 { 71 Tarjan(v); 72 Low[u]=min(Low[u],Low[v]); 73 } 74 else if(vis[v]) 75 Low[u]=min(Low[u],Dfn[v]); 76 } 77 78 if(Low[u]==Dfn[u]) 79 { 80 jishu++; 81 do 82 { 83 v=q[t--]; 84 F[v]=jishu; 85 vis[v]=0; 86 Num[jishu]=Num[jishu]+1; 87 }while(v!=u); 88 } 89 } 90 91 void Rebuild() 92 { 93 num=0; 94 for(int u=1;u<=n;u++) 95 { 96 for(int e=First[u];e;e=Next[e]) 97 { 98 int v=Go[e]; 99 if(F[u]!=F[v]) 100 { 101 a[++num].u=F[u]; 102 a[num].v=F[v]; 103 } 104 } 105 } 106 107 sort(a+1,a+num+1,cmp); 108 num=unique(a+1,a+num+1,rule)-1-a; 109 110 for(int i=1;i<=num;i++) 111 { 112 Add_edge(a[i].u,a[i].v); 113 } 114 } 115 116 void Topufirst() 117 { 118 for(int v=1;v<=jishu;v++) 119 { 120 if(!Input[v]) q[++wei]=v; 121 dist[v]=Num[v]; 122 f[v]=1; 123 Ans=max(Ans,dist[v]); 124 } 125 } 126 127 void TopuA() 128 { 129 while(tou<wei) 130 { 131 int u=q[++tou]; 132 for(int e=first[u];e;e=next[e]) 133 { 134 int v=go[e]; 135 if(dist[v]<dist[u]+Num[v]) 136 { 137 dist[v]=dist[u]+Num[v]; 138 f[v]=f[u]; 139 Ans=max(Ans,dist[v]); 140 } 141 else 142 if(dist[v]==dist[u]+Num[v]) f[v]=(f[v]+f[u])%MOD; 143 if(!(--Input[v])) q[++wei]=v; 144 } 145 } 146 } 147 148 int main() 149 { 150 n=get(); m=get(); MOD=get(); 151 for(int i=1;i<=m;i++) 152 { 153 x=get(); y=get(); 154 Add(x,y); 155 } 156 157 for(int i=1;i<=n;i++) 158 if(!Dfn[i]) Tarjan(i); 159 160 tot=0; 161 Rebuild(); 162 163 tou=0; wei=0; 164 Topufirst(); TopuA(); 165 166 tot=0; 167 for(int i=1;i<=jishu;i++) 168 if(dist[i]==Ans) tot=(tot+f[i])%MOD; 169 170 printf("%d\n%d",Ans,tot); 171 }