Acwing 1175.最大连通子图
题意
一个有向图
G
=
(
V
,
E
)
G=(V, E)
G=(V,E) 称为半连通的 (Semi-Connected),如果满足:
∀
u
,
v
∈
V
\forall u, v \in V
∀u,v∈V ,满足
u
→
v
u \rightarrow v
u→v 或
v
→
u
v \rightarrow u
v→u ,即 对于图中任意两点
u
,
v
u, v
u,v ,存在一条
u
u
u 到
v
v
v 的有向路径或者从
v
v
v 到
u
u
u 的有向路径。
若
G
′
=
(
V
′
,
E
′
)
G^{\prime}=\left(V^{\prime}, E^{\prime}\right)
G′=(V′,E′) 满足,
E
′
E^{\prime}
E′ 是
E
E
E 中所有和
V
′
V^{\prime}
V′ 有关的边,则称
G
′
G^{\prime}
G′ 是
G
G
G 的一个导出子图。 若
G
′
G^{\prime}
G′ 是
G
G
G 的导出子图,且
G
′
G^{\prime}
G′ 半连通,则称
G
′
G^{\prime}
G′ 为
G
G
G 的半连通子图。
若
G
′
G^{\prime}
G′ 是
G
G
G 所有半连通子图中包含节点数最多的,则称
G
′
G^{\prime}
G′ 是
G
G
G 的最大半连通子图。
给定一个有向图
G
G
G ,请求出
G
G
G 的最大半连通子图拥有的节点数
K
K
K ,以及不同的最大半连通子图的数目
C
C
C 。 由于
C
C
C 可能比较大,仅要求输出
C
C
C 对
X
X
X 的余数。
输入格式
第一行包含三个整数
N
,
M
,
X
。
N
,
M
N, M, X 。 N, M
N,M,X。N,M 分别表示图
G
G
G 的点数与边数,
X
X
X 的意义如上文所述;
接下来
M
M
M 行,每行两个正整数
a
,
b
a, b
a,b ,表示一条有向边
(
a
,
b
)
(a, b)
(a,b) 。
图中的每个点将编号为 1 到
N
N
N ,保证输入中同一个
(
a
,
b
)
(a, b)
(a,b) 不会出现两次。
输出格式
应包含两行。
第一行包含一个整数
K
K
K ,第二行包含整数
C
m
o
d
X
o
C \bmod X_{\mathrm{o}}
CmodXo
数据范围
1
≤
N
≤
1
0
5
1 \leq N \leq 10^{5}
1≤N≤105
1
≤
M
≤
1
0
6
1 \leq M \leq 10^{6}
1≤M≤106
1
≤
X
≤
1
0
8
1 \leq X \leq 10^{8}
1≤X≤108
思路
半连通:存在 u → v u \rightarrow v u→v 的路径 或者存在 v → u v \rightarrow u v→u 的路径
导出子图:若 G ′ = ( V ′ , E ′ ) G' = (V',E') G′=(V′,E′) 满足, E ′ E' E′ 是 E E E 中所有和 V ′ V' V′ 有关的边,则称 G ′ G' G′ 为 G G G 的一个导出子图
半连通子图:若 G ′ G' G′ 是 G G G 的导出子图,并且 G ′ G' G′ 半连通,那么称 G ′ G' G′ 为 G G G 的半连通子图
最大半连通子图:对于所有符合条件的半连通子图 G ′ G' G′ 有 ∣ G ′ ′ ∣ > = ∣ G ′ ∣ |G''| >= |G'| ∣G′′∣>=∣G′∣ 则 G ′ ′ G'' G′′ 称为原图 G G G 的最大连通子图
强连通分量必然是半连通
求解步骤:
-
先用tarjan算法求出强连通分量并缩点
-
建图 给边判重(给边判重的原因是 两个半连通子图不相同当且仅当两个子图有某些点不同 而当两个半连通子图只有边不同时,我们认为它们相等)
-
拓扑图上求最大半连通子图 ⟺ \iff ⟺ 求最长无分叉链
-
求最长无分叉链 ⟺ \iff ⟺ 拓扑图上的最长路(scc中点的数量为权重)
代码
// 题目给的 M 为 1e6 但是我们要建两个图 所以 设 M 为 2e6
const int N = 1e5 + 10, M = 2e6 + 10;
int n, m, mod;
int h[N],hs[N],e[M],ne[M],idx;
int low[N],dfn[N];
int scc,timestamp;
stack<int>stk;
bool in_stk[N];
int id[N],siz[N];
int f[N],g[N];
unordered_set<LL>S;
void add(int h[],int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void tarjan(int u){
dfn[u] = low[u] = ++timestamp;
stk.push(u),in_stk[u] = true;
for(int i = h[u];~i;i = ne[i]){
int j = e[i];
if(!dfn[j]){
tarjan(j);
low[u] = min(low[u],low[j]);
}
else if(in_stk[j]){
low[u] = min(low[u],dfn[j]);
}
}
if(dfn[u] == low[u]){
int y = 0;
++scc;
do{
y = stk.top();
stk.pop();
in_stk[y] = false;
siz[scc]++;
id[y] = scc;
}while(y != u);
}
}
void solve() {
scanf("%d%d%d",&n,&m,&mod);
memset(h,-1,sizeof h);
memset(hs,-1,sizeof hs);
while(m--){
int a,b;scanf("%d%d",&a,&b);
add(h,a,b);
}
for(int i = 1;i <= n;++i){
if(!dfn[i])
tarjan(i);
}
for(int i = 1; i <= n;++i){ // 对scc建图
for(int j = h[i];~j;j = ne[j]){
int k = e[j];
int a = id[i], b = id[k];
LL hash = a * 1000000ll + b;
if(a != b && !S.count(hash)){
add(hs,a,b);
S.insert(hash);
}
}
}
for(int i = scc;i;--i){ // scc递减的顺序为 拓扑序
if(!f[i]){
f[i] = siz[i];
g[i] = 1;
}
for(int j = hs[i];~j;j = ne[j]){
int k = e[j];
if(f[k] < f[i] + siz[k]){
f[k] = f[i] + siz[k];
g[k] = g[i];
}
else if(f[k] == f[i] + siz[k]){
g[k] = (g[k] + g[i]) % mod;
}
}
}
LL sum = 0,maxn = 0;
for(int i = 1; i <= scc;++i){
if(f[i] > maxn){
maxn = f[i];
sum = g[i];
}
else if(f[i] == maxn){
sum = (sum + g[i]) % mod;
}
}
printf("%lld\n",maxn);
printf("%lld\n",sum);
}
signed main() {
//int _; cin >> _;
//while (_--)
solve();
return 0;
}