洛谷P3387 【模板】缩点

缩点

Tarjan模板

dfs过程的每条边x→y

  • 树枝边(在搜索树中的边,y从未访问过),那么递归处理y;low[x]=min(low[x],low[y])

  • 后向边(y被访问过,且在栈中),low[x]=min(low[x],dfn[y])

  • 横叉边(y被访问过,但不在栈中),什么也不做
    对不同强连通分量重新建图,新图为DAG,在新图里拓扑排序+DP求最长链

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int maxn=400005,maxm=4000005;
    queue<int> q;
    int top=-1,num,scc_num,n,m,w[maxn],w_c[maxn],ans,dfn[maxn],
    	low[maxn],stack[maxn],c[maxn],f[maxn],du[maxn];
    bool vis[maxn];
    struct data{//存储边 便于建新图
    	int from,to;
    }node[maxn];
    struct edge{
    	#define New(p) p=&e[++top]
    	int to;edge *Nex;
    }*head[maxn],e[maxm];
    inline void add(int x,int y){
    	edge *p;New(p);p->to=y;p->Nex=head[x];head[x]=p;
    }
    void tarjan(int x){
    	dfn[x]=low[x]=++num;
    	stack[++top]=x;vis[x]=true;
    	for(edge *i=head[x];i!=NULL;i=i->Nex){
    		if(!dfn[i->to]){//树枝边
    			tarjan(i->to);
    			low[x]=min(low[x],low[i->to]);
    		}
    		else if(vis[i->to]){//返祖边
    			low[x]=min(low[x],dfn[i->to]);
    		}
    	}
    	if(dfn[x]==low[x]){//找到SCC
    		++scc_num;
    		while(int tmp=stack[top--]){
    			vis[tmp]=false;
    			c[tmp]=scc_num;
    			w_c[scc_num]+=w[tmp];//更新新图点权
    			if(x==tmp) break;
    		}
    	}
    }
    void my_clear(){
    	memset(e,0,sizeof(e));
    	memset(head,0,sizeof(head));
    	top=-1;
    }
    void topo(){
    	for(int i=1;i<=scc_num;++i) if(!du[i]){
    		q.push(i);
    		f[i]=w_c[i];//入度为0时初始化为该点点权
    	}
    	while(!q.empty()){
    		int h=q.front();q.pop();
    		for(edge *i=head[h];i!=NULL;i=i->Nex){
    			f[i->to]=max(f[i->to],f[h]+w_c[i->to]);//简单DP
    			--du[i->to];
    			if(!du[i->to]) q.push(i->to);
    		}
    	}
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i) scanf("%d",&w[i]);
    	for(int i=1,x,y;i<=m;++i){
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		node[i].from=x;
    		node[i].to=y;
    	}
    	for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    	my_clear();//清空原图
    	for(int i=1;i<=m;++i){
    		if(c[node[i].from] != c[node[i].to]){//对原来每条边 若连接的两点不在同一SCC
    			add(c[node[i].from],c[node[i].to]);//建边 (方向不变)
    			++du[c[node[i].to]];//++入度
    		}
    	}
    	topo();//拓扑排序
    	for(int i=1;i<=scc_num;++i) ans=max(ans,f[i]);
    	printf("%d",ans);
    	return 0;
    }
原文地址:https://www.cnblogs.com/yu-xing/p/10491551.html