AcWing 1175
一个有向图 G=(V,E) 称为半连通的 (Semi-Connected),如果满足:∀u,v∈V,满足 u→v 或 v→u,即对于图中任意两点 u,v,存在一条 u 到 v 的有向路径或者从 v 到 u 的有向路径。
若 G′=(V′,E′) 满足,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 到 N,保证输入中同一个 (a,b) 不会出现两次。
应包含两行。
第一行包含一个整数 K,第二行包含整数 C mod X。
1≤N≤ 1 0 5 10^5 105,
1≤M≤ 1 0 6 10^6 106,
1≤X≤ 1 0 8 10^8 108
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
3
3
先用tarjan算法求出强连通分量,将原图进行缩点,原图变成了一个有向无环图,求最大半连通分量,其实就是在缩点后的图上找出最长路(点权为该强连通分量中的结点个数),顺便同计出条数(半连通子图的个数)。
1.缩点时需要将边判重,否则会重复计算,并注意空间
2.连通分量递减的顺序一定是拓扑序,不用再做一遍拓扑排序
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 100005;
const int M = 2000005;
int head[N], nxt[M], pnt[M], E;
int dfn[N], low[N], Time;
int stac[N], top;
bool ins[N];
int scc, belong[N], Size[N];
int tsort[N], f, dp[N], cnt[N];
int head2[N];
set<long long> st;
void add_edge(int h[], int a, int b){
pnt[E] = b;
nxt[E] = h[a];
h[a] = E++;
}
void dfs(int u){
dfn[u] = low[u] = ++Time;
stac[++top] = u;
ins[u] = 1;
int i, v;
for(i = head[u]; i != -1; i = nxt[i]){
v = pnt[i];
if(!dfn[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(ins[v])
low[u] = min(low[u], dfn[v]);
}
int s;
if(dfn[u] == low[u]){
scc++;
Size[scc] = 0;
do{
s = stac[top];
top--;
ins[s] = 0;
Size[scc]++;
belong[s] = scc;
}while(s != u);
tsort[++f] = scc;
}
}
int main(){
int n, m, x, i, j, k;
int a, b;
scanf("%d%d%d", &n, &m, &x);
E = 0;
memset(head, -1, sizeof(head));
memset(head2, -1, sizeof(head2));
for(i = 1; i <= m; i++){
scanf("%d%d", &a, &b);
add_edge(head, a, b);
}
Time = 0;
top = 0;
scc = 0;
f = 0;
for(i = 1; i <= n; i++)
if(!dfn[i])
dfs(i);
long long tmp;
for(i = 1; i <= n; i++){
for(j = head[i]; j != -1; j = nxt[j]){
k = pnt[j];
if(belong[i] == belong[k]) continue;
tmp = (long long)belong[i] * N + belong[k];
if(st.count(tmp)) continue;
add_edge(head2, belong[i], belong[k]);
st.insert(tmp);
}
}
int ans1 = 0, ans2 = 0;
for(i = scc; i >= 1; i--){
if(dp[i] == 0)
cnt[i] = 1;
dp[i] += Size[i];
if(dp[i] > ans1){
ans1 = dp[i];
ans2 = cnt[i] % x;
}
else if(dp[i] == ans1)
ans2 = (ans2 + cnt[i]) % x;
for(j = head2[i]; j != -1; j = nxt[j]){
k = pnt[j];
if(dp[i] > dp[k]){
dp[k] = dp[i];
cnt[k] = cnt[i];
}
else if(dp[i] == dp[k])
cnt[k] = (cnt[k] + cnt[i]) % x;
}
}
printf("%d\n%d\n", ans1, ans2);
return 0;
}