题目描述
给出一张图,定义图的半连通子图为点集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;
}