YBTOJ C. 【例题3】最大半连通子图

思路 

拿到题后首先应该想到缩点(毕竟强连通分量一定是半联通的,这样就生成了一个有向无环图~

在一个有向无环图中的“半联通”就是一条链(因为一条链中的任意两个点都满足定义

那么第一问而言就是找一天最大链,而第二问就是找最大连的条数

注意:重新建边的时候需要去重

Code

#include<bits/stdc++.h>
#define re register
#define ll int long long
using namespace std;
const int N=1e6+10;
int read(){
	int sum=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9'){sum=(sum<<3)+(sum<<1)+(c^48);c=getchar();}
	return sum*f;
}
int n,m,mod;
struct node{
	int v,nxt;
}e[N];
struct Node{
	int u,v;
}c[N],b[N];
bool cmp(Node a,Node b){
	return(a.u<b.u||a.u==b.u&&a.v<b.v||a.u==b.u&&a.v==b.v);
}
int u[N],v[N],k,now1,now2;
int dis[N],col,scc[N];
int head[N],cnt,dfn[N],low[N],top,dep,stk[N],color[N],sum[N],rd[N],cd[N];
bool f[N];
void add(int u,int v){
	e[++cnt].v=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void tarjan(int u){
	dfn[u]=low[u]=++dep;
	stk[++top]=u;
	for(re int i=head[u];i;i=e[i].nxt){
		if(!dfn[e[i].v]){
			tarjan(e[i].v);
			low[u]=min(low[u],low[e[i].v]);
		}
		else if(!color[e[i].v]){
			low[u]=min(low[u],dfn[e[i].v]);
		}
	}
	if(low[u]==dfn[u]){
		color[u]=++col;
		scc[col]++;
		while(stk[top]!=u){
			color[stk[top]]=col;
			scc[col]++;
			top--;
		}
		top--;
	}
}
void dfs(int u,int fa){
	f[u]=true;
	if(!cd[u]){
		dis[u]=scc[u];
		sum[u]=1;
		return;
	}
	for(int i=head[u];i;i=e[i].nxt){
		if(e[i].v!=fa){
			if(!f[e[i].v]) dfs(e[i].v,u);
			if(dis[e[i].v]+scc[u]>dis[u]) dis[u]=dis[e[i].v]+scc[u],sum[u]=sum[e[i].v]%mod;
			else if(dis[e[i].v]+scc[u]==dis[u]) sum[u]=(sum[u]+sum[e[i].v])%mod;
		}
	}
}
int main(){
	n=read(),m=read(),mod=read();
	for(re int i=1;i<=m;i++){
		u[i]=read();
		v[i]=read();
		add(u[i],v[i]);
	}
	for(re int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i);
	}
	for(re int i=1;i<=m;i++){
		if(color[u[i]]!=color[v[i]]){
			k++;
			c[k].u=color[u[i]];
			c[k].v=color[v[i]];
			
		}
	}
	sort(c+1,c+1+k,cmp);
	cnt=1;
	b[cnt].u=c[1].u,b[cnt].v=c[1].v;
	now1=b[cnt].u,now2=b[cnt].v;
	for(re int i=2;i<=k;i++){
		if(c[i].u!=now1||c[i].v!=now2){
			cnt++;
			b[cnt].u=c[i].u,b[cnt].v=c[i].v;
			now1=b[cnt].u,now2=b[cnt].v;
		}
	}
	memset(head,0,sizeof(head));
	memset(e,0,sizeof(e));
	for(re int i=1;i<=cnt;i++){
		cd[b[i].u]++,rd[b[i].v]++;
		e[i].nxt=head[b[i].u];
		e[i].v=b[i].v;
		head[b[i].u]=i;
	}
	for(re int i=1;i<=col;i++){
		if(!rd[i]&&!f[i]) dfs(i,0);
	}
	int maxn=0,ans=0;
	for(re int i=1;i<=col;i++){
		if(dis[i]>maxn){
			maxn=dis[i];
			ans=sum[i];
		}
		else{
			if(maxn==dis[i]){
				ans=(ans+sum[i])%mod;
			}
		}
	}
	printf("%d\n%d",maxn,ans);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值