Note5

Tarjan 缩点

代码是洛谷P3387[缩点模板题]

题意:给一个有向点权图,重复经过一个点只算一次点权,求一条路径使得路径上的点权和最大。’
思路:将强连通分量缩点,变成一个DAG,拓扑排序求最长路

时间复杂度O(n)

#include<bits/stdc++.h>
using namespace std;
#define next Next
const int N=1e5+7,M=1e5+7;
struct Graph{
    
    
	struct Edge{
    
    
		int u,v,next;
	}e[M];
	struct Information{
    
    //存重新建边后新节点的各种信息
		int size,val,in,out;//大小,点权,入度,出度
		Information():val(0),in(0),out(0){
    
    }
	}vertex[N];
	int n,edit_n,m,dfn[N],low[N],vis[N],head[N],tot,cnt,belong[N],id[N],value[N];
	//dfn时间戳, low能追溯到的最早的栈中节点的次序号, vis是否在栈中
	//belong属于哪个强连通分量, id缩点后的新编号
	stack<int>st;
	int sum[N],ans;//路径上的点权
	void clear(){
    
    
		memset(head,0,sizeof(head));
		for(int i=1;i<=n;i++)belong[i]=i;
		tot=cnt=edit_n=0;
	}
	void add(int u,int v){
    
    e[++tot]=Edge{
    
    u,v,head[u]};head[u]=tot;}
	void Tarjan(int u){
    
    //找强连通分量
		dfn[u]=low[u]=++cnt;
		st.push(u);
		vis[u]=1;
		for(int i=head[u];i;i=e[i].next){
    
    
			if(!dfn[e[i].v]){
    
    
				Tarjan(e[i].v);
				low[u]=min(low[u],low[e[i].v]);
			}else{
    
    
				if(vis[e[i].v])
					low[u]=min(low[u],dfn[e[i].v]);
			}
		}
		int now=0;
		if(dfn[u]==low[u]){
    
    
			id[u]=++edit_n;//给当前强连通分量新的编号
			while(now!=u){
    
    
				now=st.top();st.pop();
				vertex[id[u]].size++;
				vertex[id[u]].val+=value[now];
				vis[now]=0;
				belong[now]=u;
			}
		}
	}
	void Rebuild(){
    
    //重新建边
		memset(head,0,sizeof(head));
		tot=0;
		for(int i=1;i<=m;i++){
    
    
			int u=belong[e[i].u],v=belong[e[i].v];
			if(u!=v){
    
    
				add(id[u],id[v]);
				vertex[id[u]].out++;
				vertex[id[v]].in++;
			}
		}
		swap(n,edit_n); m=tot;//新的点数和边数
	}
	void Topo(){
    
    //拓扑排序
		queue<int>q;
		for(int i=1;i<=n;i++)
			if(vertex[i].in==0){
    
    
				q.push(i);
				sum[i]=vertex[i].val;
			}
		while(!q.empty()){
    
    
			int u=q.front();q.pop();
			for(int i=head[u],v;i;i=e[i].next){
    
    
				v=e[i].v;
				sum[v]=max(sum[v],sum[u]+vertex[v].val);
				vertex[v].in--;
				if(vertex[v].in==0)q.push(v);
			}
		}
	}
	void Solve(){
    
    
		/* 缩点 */ 
		for(int i=1;i<=n;i++)
			if(!dfn[i])Tarjan(i);
		Rebuild();
		/* 拓扑排序求最大路径点权和 */
		Topo();
		for(int i=1;i<=n;i++)ans=max(ans,sum[i]);
	}
}G;
int main(){
    
    
	cin>>G.n>>G.m;
	G.clear();
	for(int i=1;i<=G.n;i++)scanf("%d",&G.value[i]);
	for(int i=1,u,v;i<=G.m;i++){
    
    
		scanf("%d%d",&u,&v);
		G.add(u,v);
	}
	G.Solve();//缩点
	printf("%d",G.ans);

}


倍增LCA

O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define next Next
const int N=1e5+7;
struct Tree{
    
    
    struct Edge{
    
    int u,v,next;}e[N<<1];
    int n,root,fa[N][30],dep[N],head[N],tot;
    void clear(){
    
    
        memset(head,0,sizeof(head));
        memset(dep,-1,sizeof(dep));
        tot=0;
    }
    void add(int u,int v){
    
    e[++tot]=Edge{
    
    u,v,head[u]};head[u]=tot;}
    void addedge(int u,int v){
    
    add(u,v);add(v,u);}
    void dfs(int u,int pre=0){
    
    
        dep[u]=dep[pre]+1;
        fa[u][0]=pre;
        for(int i=1;(1<<i)<=dep[u];i++)
            fa[u][i]=fa[fa[u][i-1]][i-1];
        for(int i=head[u];i;i=e[i].next)
            if(e[i].v!=pre)dfs(e[i].v,u);
    }
    int LCA(int u,int v){
    
    
        if(dep[u]<dep[v])swap(u,v);
        for(int i=20;i>=0;i--){
    
    
            if(dep[u]>=dep[v]+(1<<i))
                u=fa[u][i];
        }
        if(u==v)return u;
        for(int i=20;i>=0;i--)
            if(fa[u][i]!=fa[v][i]){
    
    
                u=fa[u][i];
                v=fa[v][i];
            }
        return fa[u][0];
    }
}T;
int main(){
    
    
    T.clear();
    int q;
    cin>>T.n>>q>>T.root;
    for(int i=1,u,v;i<T.n;i++){
    
    
        scanf("%d%d",&u,&v);
        T.addedge(u,v);
    }
    T.dfs(T.root);
    for(int i=1,u,v;i<=q;i++){
    
    
        scanf("%d%d",&u,&v);
        printf("%d\n",T.LCA(u,v));
    }
}

猜你喜欢

转载自blog.csdn.net/qq_45530271/article/details/108941478