1093: [ZJOI2007]最大半连通子图
Time Limit: 30 Sec Memory Limit: 162 MB
Submit: 3662 Solved: 1449
[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 ≤100000, M ≤1000000;对于100%的数据, X ≤10^8
Output
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
Sample Input
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
Sample Output
3
3
题解
两个强联通子图合并肯定是一个半联通子图。
tarjan找出所有强联通子图并缩点。
剩下DAG图按拓扑排序跑DP求出最大半联通子图即可。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<stack> #include<queue> #include<vector> using namespace std; const int N=100005,M=1000005; int n,m,x,k=1,nk=1,cnt,tot,ans1,ans2; int head[N],dfn[N],low[N],vis[N],belong[N],num[N],in[N],f[N],d[N]; stack<int>st; queue<int>q; vector<int>g[N]; struct edge{ int u,v,next; }e[M]; void addedge(int u,int v){ e[k]=(edge){u,v,head[u]}; head[u]=k++; } void tarjan(int u){ dfn[u]=++cnt; low[u]=cnt; st.push(u); vis[u]=1; int v; for(int i=head[u];i;i=e[i].next){ v=e[i].v; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v])low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]){ tot++; while(!st.empty()){ v=st.top(); st.pop(); vis[v]=0; belong[v]=tot; num[tot]++; if(u==v)break; } } } void build(){ int v; for(int i=1;i<=n;i++){ for(int j=head[i];j;j=e[j].next){ v=e[j].v; if(belong[i]!=belong[v]){ g[belong[i]].push_back(belong[v]); in[belong[v]]++; } } } } void dp(){ for(int i=1;i<=tot;i++){ if(!in[i])q.push(i); d[i]=num[i]; f[i]=1; } int u,v; while(!q.empty()){ u=q.front(); q.pop(); for(int i=0;i<g[u].size();i++){ v=g[u][i]; in[v]--; if(!in[v])q.push(v); if(vis[v]==u)continue; if(d[u]+num[v]>d[v]){ d[v]=d[u]+num[v]; f[v]=f[u]; } else if(d[u]+num[v]==d[v])f[v]=(f[v]+f[u])%x; vis[v]=u; } } } int main(){ scanf("%d%d%d",&n,&m,&x); int u,v; for(int i=1;i<=m;i++){ scanf("%d%d",&u,&v); addedge(u,v); } for(int i=1;i<=n;i++){ if(!dfn[i])tarjan(i); } build(); dp(); for(int i=1;i<=tot;i++){ if(d[i]>ans1){ ans1=d[i]; ans2=f[i]; } else if(d[i]==ans1)ans2=(ans2+f[i])%x; } printf("%d %d ",ans1,ans2); return 0; }