[ZJOI2007]最大半连通子图
题目描述
一个有向图\(G=(V,E)\)称为半连通的(Semi-Connected),如果满足:\(\forall u,v\in V\),满足\(u\leadsto v\)或\(v\leadsto u\),即对于图中任意两点\(u\),\(v\),存在一条\(u\)到\(v\)的有向路径或者从\(v\)到\(u\)的有向路径。
若\(G'=(V',E')\)满足\(V'\subseteq V\),\(E'\)是\(E\)中所有跟\(V'\)有关的边,则称\(G'\)是\(G\)的一个导出子图。若\(G'\)是\(G\)的导出子图,且\(G'\)半连通,则称\(G'\)为\(G\)的半连通子图。若\(G'\)是\(G\)所有半连通子图中包含节点数最多的,则称\(G'\)是\(G\)的最大半连通子图。给定一个有向图\(G\),请求出\(G\)的最大半连通子图拥有的节点数\(K\),以及不同的最大半连通子图的数目\(C\)。由于\(C\)可能比较大,仅要求输出\(C\)对\(X\)的余数。
输入格式
第一行包含两个整数\(N\),\(M\),\(X\)。\(N\),\(M\)分别表示图\(G\)的点数与边数,\(X\)的意义如上文所述。
接下来\(M\)行,每行两个整数\(a\),\(b\),表示一条有向边\((a,b)\)。图中的每个点将编号为\(1,2,\cdots,N\),保证输入中同一个\((a,b)\)不会出现两次。
输出格式
第一行包含一个整数\(K\)。
第二行包含整数\(C\ Mod\ X\)。
输入输出样例
输入
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
输出
3
3
说明/提示
对于\(100\%\)的数据,\(N\le 10^5\),\(M\le 10^6\),\(X\le 10^8\)。
强连通分量+动态规划
强连通分量
在有向图\(G\)中,如果两个顶点\(u_i\),\(u_j\)间有一条从\(u_i\)到\(u_j\)的有向路径,同时还有一条从\(u_j\)到\(u_i\)的有向路径,则称两个顶点强连通(strongly connected)。如果有向图\(G\)的每两个顶点都强连通,称\(G\)是一个强连通图。有向图的极大强连通子图,称为强连通分量(strongly connected components)。
显然,对于任意一个强连通分量\(S\in G\),它都是一个半连通子图,于是我们可以考虑先缩点。
动态规划
缩点之后,原图变成了一个DAG(有向无环图),显然,对于任意一条DAG中的链,它都是一个半连通子图,所以题目中的\(K\)就是DAG中最大链的顶点个数,\(C\)就是DAG中不同的最大链有多少个。
设\(size(u)\)表示顶点\(u\)代表的强连通分量\(S\)中的顶点个数,\(f(u)\)表示从顶点\(u\)开始延伸的最大链的顶点个数,\(d(u)\)表示从顶点\(u\)开始延伸的不同的最大链有多少个,则有:
\(f(u)=size(u)+max(f(v))\quad(u\to v)\)
\(d(u)=\sum d(v)\quad(u\to v,size(u)+f(v)=f(u))\)
每次从一个没有被访问过的顶点开始做深度优先搜索,回溯时计算每个顶点的\(f\),\(d\)值。\(K\),\(C\)也可以按照相同的方式进行更新。
代码
#include<map>
#include<stack>
#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN=(int)1e5+5,MAXM=(int)1e6+5;
map<int,bool>M[MAXN];
stack<int>S;
int lw[MAXN],siz[MAXN],scc[MAXN],f[MAXN],d[MAXN];
int hed1[MAXN],nxt1[MAXM],to1[MAXM],hed2[MAXN],nxt2[MAXM],to2[MAXM];
bool vis[MAXN];
int x,cnt,tim,scclen,mx,ans;
inline void add1(int a,int b){
nxt1[++cnt]=hed1[a];
to1[cnt]=b;
hed1[a]=cnt;
}
void dfs1(int u){
int dfn=lw[u]=++tim;
S.push(u);
for(register int i=hed1[u];i;i=nxt1[i]){
if(!lw[to1[i]])
dfs1(to1[i]);
if(!scc[to1[i]])
lw[u]=min(lw[u],lw[to1[i]]);
}
if(!(lw[u]^dfn)){
scclen++;
while(1){
int pnt=S.top();
S.pop();
siz[scclen]++;
scc[pnt]=scclen;
if(!(pnt^u))
break;
}
f[scclen]=siz[scclen];
d[scclen]=1;
}
}
inline void add2(int a,int b){
nxt2[++cnt]=hed2[a];
to2[cnt]=b;
hed2[a]=cnt;
}
void dfs2(int u){
vis[u]=1;
for(register int i=hed2[u];i;i=nxt2[i]){
if(!vis[to2[i]])
dfs2(to2[i]);
if(f[to2[i]]+siz[u]>f[u]){
f[u]=f[to2[i]]+siz[u];
d[u]=d[to2[i]];
}
else if(!(f[to2[i]]+siz[u]^f[u]))
(d[u]+=d[to2[i]])%=x;
}
if(f[u]>mx){
mx=f[u];
ans=d[u];
}
else if(!(f[u]^mx))
(ans+=d[u])%=x;
}
int main(){
int n,m;
scanf("%d%d%d",&n,&m,&x);
for(register int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
add1(a,b);
}
for(register int i=1;i<=n;i++)
if(!lw[i])
dfs1(i);
cnt=0;
for(register int i=1;i<=n;i++)
for(register int j=hed1[i];j;j=nxt1[j])
if(scc[i]^scc[to1[j]]&&!M[scc[i]][scc[to1[j]]]){
add2(scc[i],scc[to1[j]]);
M[scc[i]][scc[to1[j]]]=1;
}
for(register int i=1;i<=scclen;i++)
if(!vis[i])
dfs2(i);
printf("%d\n%d\n",mx,ans);
return 0;
}

浙公网安备 33010602011771号