最大半连通子图

zhangziheng 2021-12-23 20:39:05

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;

int n,m,mod;
int hd1[N],nt1[N],vr1[N],tt1;
int hd[N],nt[N],vr[N],tt;
int dfn[N],low[N],times;
bool ins[N];
stack<int> st;
int scc_size[N],id[N],scc_cnt;
int f[N],g[N],v[N],ans,tmp;

void add1(int x,int y)
{
	vr1[++tt1]=y;
	nt1[tt1]=hd1[x];
	hd1[x]=tt1;
}

void add(int x,int y)
{
	vr[++tt]=y;
	nt[tt]=hd[x];
	hd[x]=tt;
}

void tarjan(int x)
{
	dfn[x]=low[x]=++times;
	st.push(x);
	ins[x]=1;
	for(int i=hd1[x];i;i=nt1[i])
	{
		int y=vr1[i];
		if(!dfn[y]) 
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(ins[y])
		{
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(low[x]==dfn[x])
	{
		scc_cnt++;
		int val;
		do
		{
			val=st.top();
			st.pop();
			ins[val]=0;
			id[val]=scc_cnt;
			scc_size[scc_cnt]++;
		}while(val!=x);
	} 
}

int main()
{
	scanf("%d%d%d",&n,&m,&mod);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add1(x,y);
	}
	for(int i=1;i<=n;i++)//tarjan 
		if(!dfn[i]) 
			tarjan(i);
	for(int x=1;x<=n;x++)//缩点 
	{
		//初始化 
		f[x]=scc_size[x];//最大半连通子图的大小 
		g[x]=1;//方案数 
		for(int i=hd1[x];i;i=nt1[i])
		{
			int y=vr1[i];
			int a=id[x],b=id[y];
			if(a==b) continue;
			add(a,b);
		}
	} 
	for(int x=scc_cnt;x>=1;x--) //缩点后的顺序为逆拓扑序 
	{
   		for(int i=hd[x];i;i=nt[i]) 
		{
	   		int y=vr[i];
	   		int w=f[x]+scc_size[y];
	   		if(v[y]==x) continue; //重边
	   		v[y]=x; //记录转移 
	   		if(f[y]<w) 
			{
	   			f[y]=w;
	   			g[y]=g[x];
	   		} 
			else if(f[y]==w) 
			{
	   			g[y]+=g[x];
	   			g[y]%=mod;
	   		}
   		}
	}
	for(int i=1;i<=scc_cnt;i++) 
	{
		if(f[i]>ans) 
		{
			ans=f[i];
			tmp=g[i];
		}
		else if(f[i]==ans) 
		{
			tmp+=g[i];
			tmp%=mod;
		}
	}
	printf("%d\n%d",ans,tmp);
	return 0;
}