2018.11.06 bzoj1093: [ZJOI2007]最大半连通子图(缩点+拓扑排序)

时间:2021-09-26 05:36:44

传送门

先将原图缩点,缩掉之后的点权就是连通块大小。

然后用拓扑排序统计最长链数就行了。

自己yyyyyy了一下一个好一点的统计方法。

把所有缩了之后的点都连向一个虚点。

然后再跑拓扑,这样最后虚点的答案就是要求的。

代码:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=1e5+5,M=1e6+5;
int low[N],tot=0,dfn[N],n,m,mod,first[N],cnt=0,stk[N],top=0,col[N],sig=0,val[N],f[N],g[N],du[N],pred[N];
bool vis[N],in[N];
struct edge{int v,next;}e[M<<1];
vector<int>G[N];
inline void add(int u,int v){e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
inline void tarjan(int p){
	dfn[p]=low[p]=++tot,stk[++top]=p,in[p]=1;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(!dfn[v])tarjan(v),low[p]=min(low[p],low[v]);
		else if(in[v])low[p]=min(low[p],dfn[v]);
	}
	if(dfn[p]==low[p]){
		++sig;
		while(1){
			int x=stk[top--];
			++val[col[x]=sig],in[x]=0;
			if(x==p)break;
		}
	}
}
inline void topsort(){
	queue<int>q;
	for(int i=1;i<=sig;++i)if(!du[i])f[i]=val[i],g[i]=1,q.push(i);
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=G[x].size()-1;~i;--i){
			int v=G[x][i];
			--du[v];
			if(!du[v])q.push(v);
			if(pred[v]==x)continue;
			if(f[x]+val[v]>f[v])f[v]=f[x]+val[v],g[v]=g[x];
			else if(f[x]+val[v]==f[v])g[v]=(g[v]+g[x])%mod;
			pred[v]=x;
		}
	}
}
int main(){
	freopen("lx.in","r",stdin);
	n=read(),m=read(),mod=read();
	for(int i=1,u,v;i<=m;++i)u=read(),v=read(),add(u,v);
	for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
	for(int i=1;i<=sig;++i)G[i].push_back(sig+1),++du[sig+1];
	for(int i=1;i<=n;++i)for(int j=first[i];j;j=e[j].next)if(col[i]^col[e[j].v])G[col[i]].push_back(col[e[j].v]),++du[col[e[j].v]];
	topsort(),cout<<f[sig+1]<<'\n'<<g[sig+1];
	return 0;
}