最大半连通子图【ybt高效进阶3-4-3】【luogu P2272】

最大半连通子图【ybt高效进阶3-4-3】【luogu P2272】

题目大意:

给你一张图,让你求它的最大半连通子图的个数和大小,个数对一个给出的数取模。 一个子图是半连通子图,就是这个子图的任意两个点之间都有路径从一个点到另一个点。(只要能从任意一边到另一边即可,注意与强连通区分)

思路:

首先建图,然后Tarjan缩点,缩完点后图就变成了一个DAG(有向无环图?),强连通分量必然也是一个半连通子图。
这时我们发现,一条链也是一个合法的答案,所以缩点之后,因为没有环了,合法的答案必定是一条链(自己好好理解)
所以我们可以搞一个一个拓扑排序DP。
设a[i]表示每个强连通分量的节点个数,dp[i]表示以点i为终点的链的最大的点数和,ans[i]表示以i为终点的最大链的个数,那么,我们有:
{ d p [ j ] = d p [ i ] + a [ v ] , a n s [ j ] = a n s [ i ]    ( d p [ i ] + a [ j ] > d p [ j ] ) a n s [ j ] + = a n s [ i ]    ( d p [ i ] + a [ j ] = d p [ j ] ) \left\{\begin{matrix} & dp[j]=dp[i]+a[v],ans[j]=ans[i]~~ (dp[i]+a[j]>dp[j])\\ & ans[j]+=ans[i]~~ (dp[i]+a[j]=dp[j]) \end{matrix}\right. {dp[j]=dp[i]+a[v],ans[j]=ans[i]  (dp[i]+a[j]>dp[j])ans[j]+=ans[i]  (dp[i]+a[j]=dp[j])
其中j是i通向的连通块。
所以最终答案为: m a x ( f [ i ] ) max(f[i]) max(f[i]) ∑ i = 1 t o t a n s [ i ] ( f [ i ] = = m a x ( f [ i ] ) )     ( 1 < = i < = t o t ) \sum_{i=1}^{tot}ans[i](f[i]==max(f[i]))~~~(1<=i<=tot) i=1totans[i](f[i]==max(f[i]))   (1<=i<=tot)

注意,建图时要判重边,否则会多统计

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<stack>
#include<queue>
#include<vector>
#define r register
#define rep(i,x,y) for(r int i=x;i<=y;++i)
#define per(i,x,y) for(r int i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const int V=(1e6+10)*9;
ll pos,n,m,mod,x,y,now,du[V/10],cnt[V/10];
ll top,num,tot,head1[V/10],head2[V/10];
ll in[V/10],low[V/10],dfn[V/10];
ll dp[V/10],ans[V/10],res,tj;
struct node
{
	ll nxt,to;
}e1[V],e2[V];
struct edge
{
	ll x,y;
}a[V/10];
queue<ll> q;
stack<ll> s;
void add1(ll x,ll y)
{
	e1[++top]=(node){head1[x],y};
	head1[x]=top;
}
void add2(ll x,ll y)
{
	e2[++tot]=(node){head2[x],y};
	head2[x]=tot;
}
void tarjan(ll x) //tarjan缩点
{
	dfn[x]=low[x]=++pos;
	s.push(x);
	for(r ll i=head1[x];i;i=e1[i].nxt)
	{
		ll y=e1[i].to;
		if(!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(!in[y]) low[x]=min(low[x],low[y]);
	}
	if(dfn[x]==low[x])
	{
		in[x]=++now;
		cnt[now]=1;
		while(s.top()!=x)
		{
			ll y=s.top();
			++cnt[now];
			in[y]=now;
			s.pop();
		}
		s.pop();
	}
}
bool cmp(edge x,edge y) //去重排序的函数
{
	if(x.x==y.x) return x.y<y.y;
	return x.x<y.x;
}
void topsort()
{
	rep(i,1,n)
	 if(!du[i])
	 {
	 	q.push(i);
	 	dp[i]=cnt[i];
	 	res=max(dp[i],res);
	 	ans[i]=1;
	 }
	while(!q.empty() )
	{
		ll x=q.front() ;
		q.pop();
		for(r ll i=head2[x];i;i=e2[i].nxt )
		{
			ll y=e2[i].to;
			--du[y];
			if(!du[y]) q.push(y);
			if(dp[y]<dp[x]+cnt[y])//状态转移
			{
				dp[y]=dp[x]+cnt[y];
				ans[y]=ans[x];
			}
			else if(dp[y]==dp[x]+cnt[y]) 
			{
				ans[y]+=ans[x];
				ans[y]%=mod;
			}
			res=max(res,dp[y]);
		}
	}
}
int main()
{
	scanf("%lld%lld%lld",&n,&m,&mod);
	rep(i,1,m)
	{
		scanf("%lld%lld",&x,&y);
		add1(x,y);
	}
	rep(i,1,n)
	 if(!dfn[i])
	  tarjan(i);
	rep(i,1,n)
	 for(r ll j=head1[i];j;j=e1[j].nxt)
	 {
	 	ll y=e1[j].to;
	 	if(in[i]!=in[y])
	 	{
	 		a[++num].x=in[i];
	 		a[num].y=in[y];
		}
	 }
	sort(a+1,a+num+1,cmp);//去重
	rep(i,1,num)
	 if(a[i].x!=a[i-1].x||a[i].y!=a[i-1].y)//建新图
	 {
	 	add2(a[i].x,a[i].y);
	 	++du[a[i].y];
	 }
	topsort(); //拓扑排序DP
	printf("%lld\n",res);
	rep(i,1,n)
	if(dp[i]==res)
	 tj=(tj+ans[i])%mod;
	printf("%lld",tj);
 	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值