【題解】[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章