标签:pre rebuild set 记录 个数 代码 ext top turn
题目描述
给出一张图,定义图的半连通子图为点集S中任意两点u、v都存在一条u到v的简单路径或v到u的简单路径,求这张图的最大半连通子图的节点数和个数。
思路
首先显然一个强连通子图一定是半连通子图,所以我们可以先进行缩点,这样并不影响结果的判定。接下来考虑缩点后的DAG,最大半连通子图可以转化为DAG上最长链的长度,对于这个我们可以用类似最短路方法,不过由于是DAG我们可以直接进行拓扑排序时更新最短路的值,接下来再类似最短路计数用sum[i]记录到i的路径个数。
不过需要注意虽然原图中保证五重边,但缩点可能两个强连通分量间有多个有向边相连接,由于半连通子图时点集,与边无关,这会影响到最大半连通子图的计数,所以要去重。
代码
#include <bits/stdc++.h> using namespace std; const int N=1e5+10,M=1e6+10; struct Edge { int x,y; }e[M]; bool cmp(Edge a,Edge b) { if(a.x!=b.x)return a.x<b.x; else return a.y<b.y; } int head[N],nxt[M],to[M],tot; int n,m; void add_edge(int x,int y) { nxt[++tot]=head[x]; head[x]=tot; to[tot]=y; } int read() { int res=0,w=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){res=(res<<3)+(res<<1)+ch-‘0‘;ch=getchar();} return res*w; } int st[N],siz[N],dfn[N],low[N],idx,co[N],top,col; void tarjan(int u) { dfn[u]=low[u]=++idx; st[++top]=u; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(!co[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]) { co[u]=++col; ++siz[col]; while(st[top]!=u) { ++siz[col]; co[st[top]]=col; --top; } --top; } } void del() //删边 { for(int i=1;i<=m;i++) { int u=e[i].x,v=e[i].y; e[i].x=co[u];e[i].y=co[v]; } sort(e+1,e+m+1,cmp); } int in[N]; void rebuild() //重新建图 { del(); tot=0; memset(head,0,sizeof(head)); for(int i=1;i<=m;i++) { int u=e[i].x,v=e[i].y; if(u!=v&&(u!=e[i-1].x||v!=e[i-1].y)) { add_edge(u,v); ++in[v]; } } } int dis[N],sum[N],ans,mod; queue<int> q; void topo() { for(int i=1;i<=col;i++) if(in[i]==0) { q.push(i); dis[i]=siz[i]; sum[i]=1; if(dis[ans]<dis[i])ans=i; } while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=nxt[i]) { int v=to[i]; --in[v]; if(!in[v])q.push(v); if(dis[v]<dis[u]+siz[v]) { dis[v]=dis[u]+siz[v]; sum[v]=0; if(dis[ans]<dis[v])ans=v; } if(dis[v]==dis[u]+siz[v]) sum[v]=(sum[v]+sum[u])%mod; } } } int main() { n=read();m=read();mod=read(); for(int i=1;i<=m;i++) { int a=read(),b=read(); e[i].x=a;e[i].y=b; add_edge(a,b); } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); rebuild(); topo(); int cnt=0; for(int i=1;i<=col;i++) if(dis[ans]==dis[i])cnt=(cnt+sum[i])%mod; printf("%d\n%d",dis[ans],cnt); return 0; }
标签:pre rebuild set 记录 个数 代码 ext top turn
原文地址:https://www.cnblogs.com/fangbozhen/p/11728155.html