zoukankan      html  css  js  c++  java
  • AcWing 364. 网络

    Solution

    tarjan 边双缩点成一棵树,然后用并查集优化。
    然后看到很多 dalao 都写了个 树上倍增求 lca , 其实不用。

    用并查集处理出

    int par[N];
    int Find(int x) { return ((par[x]==x) ? x : par[x]=Find(par[x])); }
    // Find(x) 找到下一个 x 的祖先(包括 x)没有被缩的点。
    

    预处理出
    每个点的 father 和 深度 depth.
    然后我们可以参考树链剖分的代码,把其中的 top[x] 换成 Find(x) 来跑即可。
    转化思路

    • 刚开始时 每个点都是一条树链。
    • 加入一条边 ((x,y)) 其实是把 (x)(lca(x,y)) 的链合并成一条,(y) 同理。

    正确性:可以参考树链剖分求 lca 的证明。
    时间复杂度

    • tarjan 的复杂度 (O(n+m))
    • Query 的复杂度 , 平摊来算,每条边只会被访问一次,所以至多 (O(m))

    Code

    #include<set>
    #include<map>
    #include<cmath>
    #include<ctime>
    #include<stack>
    #include<queue>
    #include<bitset>
    #include<vector>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    
    const int N=8e5+5;
    int one[N],hs[N],idx=1;
    int ver[N],Next[N];
    void AddEdge(int h[],int a,int b)
    {
    	Next[++idx]=h[a];
    	h[a]=idx;
    	ver[idx]=b;
    }
    
    // 边双联通分量的判断 dfn[x]==low[x] 
    int dfn[N],low[N],times;
    int id[N],all;
    stack<int> S;
    
    void tarjan(int x,int from)
    {
    	dfn[x]=low[x]=++times;
    	S.push(x);
    	int i,y;
    	for(i=one[x];i;i=Next[i]) {
    		y=ver[i];
    		if(!dfn[y]) {
    			tarjan(y,i);
    			low[x]=min(low[x],low[y]);
    		}
    		else if(i!=(from^1)) 
    			low[x]=min(low[x],dfn[y]);
    	}
    	
    	if(low[x]==dfn[x]) {
    		all++;
    		while(S.top()!=x) 
    			id[S.top()]=all,S.pop();
    		id[x]=all,S.pop();
    	}
    }
    
    int par[N];
    int Find(int x) { return ((par[x]==x) ? x : par[x]=Find(par[x])); }
    // Find(x) 找到下一个 x 的祖先(包括 x)没有被缩的点。
     
    int ans;
    int dep[N],fat[N];
    void deal_first(int x,int fa)
    {
    	dep[x]=dep[fa]+1;
    	fat[x]=fa;
    	int i,y;
    	for(i=hs[x];i;i=Next[i]) {
    		y=ver[i];
    		if(y==fa) continue;
    		deal_first(y,x);
    	}
    }
    
    void TQuery(int x,int y)
    {
    	while(Find(x)!=Find(y)) {
    		if(dep[Find(x)]<dep[Find(y)]) 
    			swap(x,y);
    		ans--;
    		x=Find(x),par[x]=fat[x],x=fat[x];
    	}
    }
    
    int n,m,Q,T;
    
    int main()
    {
    //	freopen("1.in","r",stdin);
    	int i,x,y;
    	
    	while(scanf("%d%d",&n,&m),max(n,m)) {
    		memset(dfn,0,sizeof dfn);
    		memset(low,0,sizeof low);
    		memset(id,0,sizeof id);
    		memset(one,0,sizeof one);
    		memset(hs,0,sizeof hs);
    		memset(dep,0,sizeof dep);
    		memset(fat,0,sizeof fat);
    		while(S.size()) S.pop();
    		times=all=0;
    		idx=1;
    		
    		for(i=1;i<=m;i++) {
    			scanf("%d%d",&x,&y);
    			AddEdge(one,x,y),AddEdge(one,y,x);
    		}
    		
    		for(i=1;i<=n;i++) 
    			if(!dfn[i]) tarjan(i,0);
    		
    		for(x=1;x<=n;x++) 
    			for(i=one[x];i;i=Next[i]) {
    				y=ver[i];
    				if(id[x]!=id[y]) AddEdge(hs,id[x],id[y]);
    			} 
    		ans=all-1;
    		for(i=1;i<=all;i++) par[i]=i;
    		deal_first(1,0);
    		
    		scanf("%d",&Q);
    		printf("Case %d:
    ",++T);
    		while(Q--) {
    			scanf("%d%d",&x,&y);
    			TQuery(id[x],id[y]);
    			printf("%d
    ",ans);	
    		}
    		printf("
    ");
    	}
    	return 0;
    }
    

    这里放上 AcWing 2568. 树链剖分 的部分代码用于对比。

    LL TQuery(int x,int y)
    {
    	LL res=0;
    	while(top[x]!=top[y]) {
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
    		res+=Prefix(dfn[x])-Prefix(dfn[top[x]]-1);
    		x=top[x],x=fat[x];
    	}
    	if(dep[x]<dep[y]) swap(x,y);
    	res+=Prefix(dfn[x])-Prefix(dfn[y]-1);
    	return res;
    }
    
  • 相关阅读:
    java泛型
    java集合
    java面向对象
    java常用类
    二分查找
    递归
    选择排序+冒泡排序
    threejs入门简单例子
    css表格合并边框以及单元格宽度计算方式
    Spring Boot 有哪些优点?
  • 原文地址:https://www.cnblogs.com/cjl-world/p/14054084.html
Copyright © 2011-2022 走看看