【模板】缩点(crxis 缩点)

posted on 2021-11-27 22:37:04 | under 模板 | source

sto crxis

注:不是 tarjan

void crxis(int u){
	for(int i=g.head[u];i;i=g.nxt[i]){
		int v=g[i].v;
		if(dep[v]==0){
			dep[v]=dep[u]+1;
			crxis(v);
		}
		if(dep[s.find(v)]>0){
			int x=s.find(u),y=s.find(v);
			if(dep[x]>dep[y]) swap(x,y);
			s.merge(x,y);
		}
	}
	dep[u]*=-1;
}

2023 年补充:并查集的过程等价于 tarjan 中 \(low_u=\min(low_u,low_v)\)

大致原理:首先,我们的目的是把环缩成点。记 \(dep=0\) 为未访问,\(dep>0\) 为正在访问中的点的深度,\(dep<0\) 为访问完毕。先 dfs 一遍图,这时候会有一些奇怪的边带我们访问到 \(dep>0\) 的点,显然这形成一个环,用 dsu 合并这个环中的点。往回退的时候,如何判断需不需要合并呢?判一下 \(v\) 在 dsu 中的祖先,如果这个祖先正在被访问,说明是环,合并。还有一个问题,合并时祖先选谁?应该选深度小的,因为选深度大的,判断是否在环中就会出现访问完毕的情况,环不能再扩大。

依次合并 \((4\to 2),(4\to 3),(3\to 2)\)

具体实现:图可能不联通,鉴于此我们遍历所有点,如果还没访问就 crxis 一遍。缩点时祖先相同的为同一点,会出现很多自环和重边,记得干掉它们。

下面以 [ZJOI2007] 最大半联通子图 为例,给出代码实现:

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define LOCAL
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
int P=998244353;
template<int N> struct dsu{
	int fa[N+10],cnt;
	dsu(int n=N):cnt(n){for(int i=1;i<=N;i++) fa[i]=i;}
	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	void merge(int x,int y){if(x=find(x),y=find(y),x!=y) cnt--,fa[y]=x;}
	bool query(int x,int y){return find(x)==find(y);}
};
template<int N,int M,class T=int> struct graph{
	int cnt,head[N+10],nxt[M*2+10];
	struct edge{
		int u,v;T w;
		edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
		bool operator<(edge b){if(u!=b.u) return u<b.u;if(v!=b.v) return v<b.v;return w<b.w;}
		bool operator==(edge b){return u==b.u&&v==b.v;}
	} e[M*2+10];
	edge operator[](int i){return e[i];}
	graph():cnt(0){memset(head,0,sizeof head);}
	void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
	void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
	void remove(){
		sort(e+1,e+cnt+1);
		int m=unique(e+1,e+cnt+1)-e-1;
		cnt=0,memset(head,0,sizeof head);
		for(int i=1;i<=m;i++) if(e[i].u!=e[i].v) add(e[i].u,e[i].v,e[i].w);
	}
	template<class Dsu> void assign(Dsu s){
		int m=cnt;
		cnt=0,memset(head,0,sizeof head);
		for(int i=1;i<=m;i++){
			int u=s.find(e[i].u),v=s.find(e[i].v);
			if(u!=v) add(u,v,e[i].w);
		}
	}
};
int n,m,dep[100010],siz[100010],inn[100010];
int f[100010],cnt[100010];
graph<100010,1000010> g;
dsu<100010> s;
queue<int> q;
void crxis(int u){
	for(int i=g.head[u];i;i=g.nxt[i]){
		int v=g[i].v;
		if(dep[v]==0){
			dep[v]=dep[u]+1;
			crxis(v);
		}
		if(dep[s.find(v)]>0){
			int x=s.find(u),y=s.find(v);
			if(dep[x]>dep[y]) swap(x,y);
			s.merge(x,y);
		}
	}
	dep[u]*=-1;
}
void toposort(){
	memset(f,-0x3f,sizeof f);
	for(int i=1;i<=g.cnt;i++) inn[g[i].v]++;
	for(int i=1;i<=n;i++) if(!inn[i]&&siz[i]) f[i]=siz[i],cnt[i]=1%P,q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=g.head[u];i;i=g.nxt[i]){
			int v=g[i].v;
			if(f[u]+siz[v]>f[v]){
				f[v]=f[u]+siz[v];
				cnt[v]=cnt[u]%P;
			}else if(f[u]+siz[v]==f[v]) cnt[v]=(cnt[v]+cnt[u])%P;
			if(!--inn[v]) q.push(v);
		}
	}
}
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	scanf("%d%d%d",&n,&m,&P);
	for(int i=1;i<=m;i++){int u,v;
		scanf("%d%d",&u,&v);
		g.add(u,v);
	}
	g.remove();
	for(int i=1;i<=n;i++) if(!dep[i]) crxis(dep[i]=i);
	g.assign(s);
	g.remove();
	for(int i=1;i<=n;i++) siz[s.find(i)]++;
	int ans=0,tot=0;
	toposort();
	for(int i=1;i<=n;i++){
		debug("node#%d:",i);
		if(siz[i]) debug("siz=%d,f=%d,cnt=%d\n",siz[i],f[i],cnt[i]);
		else debug("(empty)\n");
		if(f[ans]<f[i]&&siz[i]) ans=i,tot=cnt[i]%P;
		else if(f[ans]==f[i]&&siz[i]) tot=(tot+cnt[i])%P;
	}
	printf("%d\n%d\n",f[ans],tot%P);
	return 0;
}
posted @ 2022-11-06 19:16  caijianhong  阅读(29)  评论(0编辑  收藏  举报