#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10, M = 2e6 + 10;
int n, m, mod;
int h[N], e[M], ne[M], idx;
int hs[N]; //用来构建强连通分量为点的图
int low[N], dfn[N], timep; //求强连通分量
int stk[N], top;
bool is_stk[N];
int id[N];
int scc_cnt;
int f[N], g[N]; //分别记录点数和方案数
int Size[N];
void add(int h[], int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++ ;
}
void tarjan(int u){
low[u] = dfn[u] = ++ timep;
stk[ ++ top] = u;
is_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(is_stk[j]) low[u] = min(low[u], dfn[j]);
}
if(dfn[u] == low[u]){
int y;
++ scc_cnt;
do{
y = stk[top -- ];
id[y] = scc_cnt;
Size[scc_cnt] ++ ;
is_stk[y] = false;
}while(u != y);
}
}
int main()
{
cin>>n>>m>>mod;
memset(h, -1, sizeof h);
memset(hs, -1, sizeof hs);
for(int i = 0; i < m; i ++ ){
int a, b;
cin>>a>>b;
add(h, a, b);
}
for(int i = 1; i <= n; i ++ ){
if(!dfn[i]){
tarjan(i);
}
}
//根据最强连通分量建图
unordered_set<ll>S; //计算点数和方案数的时候是按照点进行加和,
//重边会导致多条路径相同的边相同的点被算多次,影响结果,所以要判重
for(int i = 1; i <= n; i ++ ){
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_cnt; i; i -- ){
if(!f[i]){
f[i] = Size[i];
g[i] = 1;
}
for(int j = hs[i]; ~j; j = ne[j]){
int k = e[j];
if(f[k] < f[i] + Size[k]){
f[k] = f[i] + Size[k];
g[k] = g[i]; //更新方案数,直接由i点到达k点,所以不存在增加方案数的情况
}
else if(f[k] == f[i] + Size[k]) g[k] = (g[k] + g[i]) % mod; //两个点的状态点数相同,所以方案数累加
}
}
int sum = 0, op = 0;
for(int i = 1; i <= scc_cnt; i ++ ){
if(f[i] > op){
op = f[i];
sum = g[i];
}
else if(op == f[i]) sum = (sum + g[i]) % mod;
}
cout<<op<<endl;
cout<<sum<<endl;
return 0;
}
AcWing 1175. 最大半连通子图 题解(tarjan缩点拓扑序dp)
最新推荐文章于 2024-11-05 22:09:57 发布
本文介绍了一种求解最大半连通子图的问题,通过Tarjan算法求解强连通分量,并根据强连通分量重新构建图,最终找到具有最大点数的半连通子图。在解决过程中,使用了动态规划优化方案数的计算,避免了重复计数。代码实现中涉及到图的邻接表存储、哈希表判重等技巧。
摘要由CSDN通过智能技术生成