1093: [ZJOI2007]最大半连通子图
Time Limit: 30 Sec Memory Limit: 162 MBSubmit: 2767 Solved: 1095
[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)不会出现两次。N ≤1
00000, M ≤1000000;对于100%的数据, X ≤10^8
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
Sample Input
1 2
2 1
1 3
2 4
5 6
6 4
Sample Output
3
HINT
Source
先缩点,重新建图,使其成为一个 DAG,
DAG 中每个点有一个点权表示这个点是原图中的几个点缩成的
新图中的一个最大半连通子图,必然是新图中的一个最长链(点权和最大),
知道了这点之后,DP 就行了,
类似于树形 DP,先求出从每个点出发,能走的最长链是多长,统计最长的那条就是最大半连通子图的点的数量了,
至于怎么求有多少个最大半连通子图,也是一样的 DP 就行,在上一步的 DP 之后,再 DP 一遍,统计每个点出发能走出多少条最长链,最后统计求和即可
1 /* 2 Problem: 3 OJ: 4 User: S.B.S. 5 Time: 6 Memory: 7 Length: 8 */ 9 #include<iostream> 10 #include<cstdio> 11 #include<cstring> 12 #include<cmath> 13 #include<algorithm> 14 #include<queue> 15 #include<cstdlib> 16 #include<iomanip> 17 #include<cassert> 18 #include<climits> 19 #include<functional> 20 #include<bitset> 21 #include<vector> 22 #include<list> 23 #include<map> 24 #define F(i,j,k) for(int i=j;i<=k;i++) 25 #define M(a,b) memset(a,b,sizeof(a)) 26 #define FF(i,j,k) for(int i=j;i>=k;i--) 27 #define BUG system("pause") 28 #define maxn 200000 29 #define inf 0x3f3f3f3f 30 #define maxm 5000000 31 //#define LOCAL 32 using namespace std; 33 int read(){ 34 int x=0,f=1;char ch=getchar(); 35 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 36 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 37 return x*f; 38 } 39 int head[maxn],to[maxm],next[maxm]; 40 int dfn[maxn],low[maxn]; 41 int st[maxm],ed[maxm]; 42 int n,m,cnt,ans,anss,mod; 43 int tot,belong[maxn],t,p,stk[maxn],val[maxn]; 44 bool instk[maxn]; 45 int num[maxn],in[maxn],dp[maxn],q[maxm],vis[maxn]; 46 inline void add(int u,int v) 47 { 48 to[cnt]=v; 49 next[cnt]=head[u]; 50 head[u]=cnt++; 51 } 52 inline void init() 53 { 54 memset(head,-1,sizeof(head));cnt=0; 55 cin>>n>>m>>mod; 56 for(int i=1;i<=m;i++) 57 { 58 cin>>st[i]>>ed[i]; 59 add(st[i],ed[i]); 60 } 61 } 62 inline void dfs(int u) 63 { 64 low[u]=dfn[u]=++t; 65 stk[++p]=u; instk[u]=true; 66 for(int i=head[u];i!=-1;i=next[i]) 67 { 68 if(!dfn[to[i]]) 69 { 70 dfs(to[i]); 71 low[u]=min(low[u],low[to[i]]); 72 } 73 else if(instk[to[i]]) low[u]=min(low[u],dfn[to[i]]); 74 } 75 if(dfn[u]==low[u]) 76 { 77 tot++; 78 int tmp=-1; 79 while(tmp!=u) 80 { 81 tmp=stk[p--]; 82 belong[tmp]=tot; 83 val[tot]++; 84 instk[tmp]=false; 85 } 86 } 87 } 88 inline void topsort() 89 { 90 int h=1,t=1,u; 91 for(int i=1;i<=tot;i++) 92 if(in[i]==0) 93 { 94 q[t++]=i; 95 dp[i]=val[i]; 96 num[i]=1; 97 } 98 while(h<t) 99 { 100 u=q[h++]; 101 for(int i=head[u];i!=-1;i=next[i]) 102 { 103 in[to[i]]--; 104 if(in[to[i]]==0) q[t++]=to[i]; 105 if(vis[to[i]]==u) continue; 106 if(dp[to[i]]<dp[u]+val[to[i]]) 107 { 108 dp[to[i]]=dp[u]+val[to[i]]; 109 num[to[i]]=num[u]; 110 } 111 else if(dp[to[i]]==dp[u]+val[to[i]]) 112 { 113 num[to[i]]=(num[to[i]]+num[u])%mod; 114 } 115 vis[to[i]]=u; 116 } 117 } 118 } 119 inline void go() 120 { 121 for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); 122 memset(head,-1,sizeof(head));cnt=0; 123 for(int i=1;i<=m;i++) 124 if(belong[st[i]]!=belong[ed[i]]) 125 { 126 add(belong[st[i]],belong[ed[i]]); 127 in[belong[ed[i]]]++; 128 } 129 topsort(); 130 for(int i=1;i<=tot;i++) 131 { 132 if(dp[i]>ans) 133 { 134 ans=dp[i]; 135 anss=num[i]; 136 } 137 else if(dp[i]==ans) anss=(anss+num[i])%mod; 138 } 139 cout<<ans<<endl<<anss<<endl; 140 } 141 int main() 142 { 143 std::ios::sync_with_stdio(false);//cout<<setiosflags(ios::fixed)<<setprecision(1)<<y; 144 #ifdef LOCAL 145 freopen("data.in","r",stdin); 146 freopen("data.out","w",stdout); 147 #endif 148 init(); 149 go(); 150 return 0; 151 }