D160 SCC 缩点+拓扑 Tarjan 算法 P2272 [ZJOI2007] 最大半连通子图

D160 SCC 缩点+拓扑 Tarjan 算法 P2272 [ZJOI2007] 最大半连通子图_哔哩哔哩_bilibili

 

P2272 [ZJOI2007] 最大半连通子图 - 洛谷

给了个有向有环图,选出一些点,满足两两至少单向可达,求选出点数的最大值,以及最大值的方案数

思路

Tarjan 缩点后,变成 DAG 图,题目就是求点数最多的最长路,以及最长路的条数

按缩点编号递减的顺序就是拓扑序,对拓扑序 DP,求最长路 $d[]$

(1)如果最长路更长,那么更新最长路 $d[y]=d[x]+siz[y]$,同时,继承方案数 $f[y]=f[x]$

(2)如果最长路相等,那么只更新方案数 $f[y]=f[y]+f[x]$

参考:D100 最短路条数 Dijkstra 算法 P1608 路径统计 - 董晓 - 博客园

注意:缩点后会在缩点之间出现重边,重边对最长路的转移不会产生影响,但是对方案数会重复统计

所以对缩点重新建图时,要用哈希表判重,去掉缩点之间的重边

如图,4 个点满足两两之间至少单向可达,缩点后变成两个点,如果不去掉缩点之间的重边,方案数会统计成 2

image

最后,枚举缩点,累加最长路的方案数

相关板子:

D15【模板】SCC 缩点 Tarjan 算法 - 董晓 - 博客园

 

// SCC 缩点 Tarjan 算法 O(N)
#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int N=100010;
vector<int> e[N],ee[N];
int n,m,mod;
int dfn[N],low[N],stk[N],top,scc[N],siz[N],cnt;
int d[N],f[N],mx,sum;

void tarjan(int x){ //SCC缩点
  dfn[x]=low[x]=++dfn[0]; stk[++top]=x;
  for(auto y:e[x]){
    if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
    else if(!scc[y]) low[x]=min(low[x],dfn[y]); 
  }

  if(dfn[x]==low[x]){
    ++cnt;
    while(stk[top+1]!=x) scc[stk[top--]]=cnt,siz[cnt]++;
  }
}
int main(){
  scanf("%d%d%d",&n,&m,&mod);
  for(int a,b;m--;) scanf("%d%d",&a,&b),e[a].push_back(b);
  
  for(int i=1; i<=n; i++)if(!dfn[i]) tarjan(i); //SCC缩点
  
  unordered_set<ll> S; //哈希表判重边
  for(int x=1; x<=n; x++)for(auto y:e[x]){ //枚举每个点的邻接点
    int a=scc[x],b=scc[y];
    ll hash=a*1000000ll+b; //端点的哈希值
    if(a!=b && !S.count(hash)){ //不在一个SCC且边(a,b)没被加过
      ee[a].push_back(b); //缩点之间连边
      S.insert(hash); //记录哈希值
    }
  }
  
  for(int x=cnt; x; x--){ //枚举缩点
    if(d[x]==0){ //拓扑图起点
      d[x]=siz[x];
      mx=max(mx,d[x]);
      f[x]=1;
    }
    for(auto y:ee[x]){ //枚举邻接点
      if(d[y]<d[x]+siz[y]){
        d[y]=d[x]+siz[y]; //更新最长路
        mx=max(mx,d[y]);
        f[y]=f[x]; //继承方案数
      }
      else if(d[y]==d[x]+siz[y]){
        f[y]=(f[y]+f[x])%mod; //更新方案数
      }
    }
  }
  
  for(int i=1; i<=cnt; i++) //枚举缩点
    if(d[i]==mx) sum=(sum+f[i])%mod; //累加最长路的方案数
  printf("%d\n%d\n",mx,sum);
}

 

posted @ 2026-05-19 10:11  董晓  阅读(53)  评论(0)    收藏  举报