2017.9.2 最大半联通子图 思考记录

本文介绍了一种解决半联通子图中寻找包含最多节点路径的问题。通过Tarjan算法进行点的压缩,并利用拓扑排序找到最优路径。文章详细展示了实现过程及代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个题一看上去似乎很难,,   半联通子图根本就没听说过啊

但经过一段时间的思考之后似乎模型挺直接的、

但是其实,他就是问你一个选点最多的路径、 因为如果这些点如果不能能构成一条路径的话,不在路径上的点一定会以一个方向走入这条路径来 会导致相反方向的点不能到达



对于能互达的点,他们发出的任意一条边都可以作为路径,因此tarjan缩点  然后用拓扑序找路径即可

2A(第一遍MLE、)

码:


#include<iostream>
#include<cstdio>
using namespace std;
#include<vector>
#include<queue>
#include<cstring>
vector<int>v[100005];
queue<int>q;
#define N 1000005
int dis,hou[N],xia[N],zhong[N],qi[N],d[N],s[N],sta[N],top,tot,cnt,jh[N],ru[N],f[N][3],pd[N],n,m,x,max1,max2;
void jian(int x,int y)
{
	++tot;hou[tot]=xia[x];xia[x]=tot;zhong[tot]=y;qi[tot]=x;	
}
void dfs(int o)
{
	int i;
	d[o]=++dis;
	s[o]=dis;
	sta[++top]=o;	
	for(i=xia[o];i!=-1;i=hou[i])
	{
		int nd=zhong[i];
		//if(nd==fu)continue;
		if(d[nd]==0)
		{			
			  dfs(nd);s[o]=min(s[o],s[nd]);
		}else if(jh[nd]==0)s[o]=min(s[o],s[nd]);		
	}
	if(s[o]==d[o])
	{    ++cnt;
		while(sta[top]!=o)
		{
		jh[sta[top]]=cnt;
		v[cnt].push_back(sta[top]);
		top--;		
		}	
		jh[sta[top]]=cnt;
		v[cnt].push_back(sta[top]);
		top--;		
	}	
}
void dp()
{
	int i,j;
	for(i=1;i<=m;i++)
	{
		if(jh[qi[i]]!=jh[zhong[i]])ru[jh[zhong[i]]]++;		
	}
	for(i=1;i<=cnt;i++)
	{
		if(ru[i]==0)q.push(i);
	}
	while(!q.empty())
	{
		int st=q.front();
		f[st][0]+=v[st].size();
		if(f[st][1]==0)f[st][1]=1;
		q.pop();
		for(i=0;i<v[st].size();i++)
		{
			int st2=v[st][i];
		for(j=xia[st2];j!=-1;j=hou[j])
		{
			int nd=zhong[j];
			if(jh[nd]==st)continue;
			ru[jh[nd]]--;
	    if(pd[jh[nd]]!=st)
		{
			
			if(f[jh[nd]][0]<f[st][0])
			{
					f[jh[nd]][0]=f[st][0];	f[jh[nd]][1]=f[st][1];
			}else if(f[jh[nd]][0]==f[st][0])
			{
				f[jh[nd]][1]+=f[st][1];f[jh[nd]][1]%=x;
			}
		pd[jh[nd]]=st;	
		}	
		if(ru[jh[nd]]==0)
		{
			q.push(jh[nd]);
		}
		}	
		}	
	}	
}
int main()
{
	memset(xia,-1,sizeof(xia));
	int i,a,b;
	scanf("%d%d%d",&n,&m,&x);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		jian(a,b);
	ru[b]++;
	}
	for(i=1;i<=n;i++)
	if(jh[i]==0)dfs(i);
	memset(ru,0,sizeof(ru));
	dp();
	for(i=1;i<=cnt;i++)
	{
		if(max1<f[i][0])
{
	max1=f[i][0];
	max2=f[i][1];	
}else 	if(max1==f[i][0])max2+=f[i][1],max2%=x;
	}
	printf("%d\n%d",max1,max2);
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值