其他分享
首页 > 其他分享> > 【题解】[ZJOI2007]最大半连通子图

【题解】[ZJOI2007]最大半连通子图

作者:互联网

[ZJOI2007]最大半连通子图

\(\text{Solution:}\)

首先考虑何时满足题目中所说的最大半连通子图。

先把强连通分量缩起来应该是毋庸置疑的一步了。考虑如何从一个强连通分量来拓展到半连通分量。

推论1:如果一张缩完点的图是半连通图,那么它的拓扑序一定唯一。

\(Proof:\) 假定拓扑序不唯一,也就是在 \(bfs\) 的过程中,队列同时存在了两个点,那么这两个点必然是无法相互到达的。证毕。

那么,有了这么一个推论,我们就可以考虑如何解题了:先考虑如何计算最大的节点数。我们缩完点之后令强连通分量的 \(siz\) 为其连通块大小,问题就转化为了有向图求最长链了。

那么方案数咋求?考虑设 \(f_i,g_i\) 分别表示到点 \(i\) 的最大节点数和方案数。如果更新了 \(f\) 就把 \(g\) 置为 \(0,\) 否则直接累加。比当前 \(f\) 小的就可以跳过了。

注意边的去重。可以去枚举原图每次清空数组做到无 $\log $ 判重,但是笔者比较懒直接上 map 了。

注意,计算 \(siz\) 不能取模,更新 \(g\) 的时候要取模。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=4e5+10;
const int M=4e6+10;
struct E{int nxt,to;}e[M],edge[M];
int head[N],Head[N],tot,tto,mod;
map<int,map<int,int> >mp;
inline int read(){
	char ch=getchar();int nn=0,ssss=1;
	while(ch<'0'||ch>'9'){if(ch=='-')ssss*=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){nn=nn*10+(ch-'0');ch=getchar();}
	return nn*ssss;
}
inline int Add(int x,int y){return (x+y+mod)%mod;}
inline int Mul(int x,int y){return 1ll*x*y%mod;}
inline void link(int x,int y,int w=0){
	if(w){
		edge[++tto]=(E){Head[x],y};
		Head[x]=tto;
		return;
	}
	e[++tot]=(E){head[x],y};
	head[x]=tot;
}
int dfn[N],low[N],st[N],top,inst[N],dfstime;
int c[N],scc,siz[N],in[N],n,m;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
void tarjan(int x){
	low[x]=dfn[x]=++dfstime;
	inst[x]=1;st[++top]=x;
	for(int i=head[x];i;i=e[i].nxt){
		int j=e[i].to;
		if(!dfn[j]){
			tarjan(j);
			low[x]=Min(low[x],low[j]);
		}
		else if(inst[j])low[x]=Min(low[x],dfn[j]);
	}
	if(dfn[x]==low[x]){
		int y=-1;
		++scc;
		while(y=st[top--]){
			siz[scc]++;
			c[y]=scc;
			inst[y]=0;
			if(x==y)break;
		}
	}
}
struct Rem{int u,v;}rem[M];
int f[N],g[N];
void BFS(){
	queue<int>q;
	for(int i=1;i<=scc;++i)if(!in[i])q.push(i);
	for(int i=1;i<=scc;++i)if(!in[i])f[i]=siz[i],g[i]=1;
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=Head[x];i;i=edge[i].nxt){
			int j=edge[i].to;
			in[j]--;
			if(!in[j])q.push(j);
			int v=f[x]+siz[j];
			if(v<f[j])continue;
			if(v>f[j]){
				f[j]=v;
				g[j]=g[x];
				continue;
			}
			if(v==f[j]){
				g[j]+=g[x];
				g[j]%=mod;
				continue;
			}
		}
	}
}
void solve(){
	int mx=-1;
	for(int i=1;i<=scc;++i)mx=Max(mx,f[i]);
	printf("%lld\n",mx);
	int res=0;
	for(int i=1;i<=scc;++i)if(f[i]==mx)res+=g[i],res%=mod;
	printf("%lld\n",res);
}
signed main(){
	freopen("semi5.in","r",stdin);
	n=read();m=read();mod=read();
	for(int i=1;i<=m;++i){
		int x=read(),y=read();
		link(x,y);
		rem[i]=(Rem){x,y};
	}
	for(int i=1;i<=n;++i)if(!dfn[i])top=0,tarjan(i);
	for(int i=1;i<=m;++i){
		int u=c[rem[i].u];
		int v=c[rem[i].v];
		if(u==v)continue;
		if(mp[u][v])continue;
		in[v]++;
		mp[u][v]=1;
		link(u,v,1);
	}
	BFS();
	solve();
	return 0;
}

标签:连通,ch,int,题解,子图,ZJOI2007,考虑,分量
来源: https://www.cnblogs.com/h-lka/p/15334077.html