POJ-3694 Network (condensation point of edge connected components + naive LCA on dfn + dynamic contraction point of concatenation)

http://poj.org/problem?id=3694


First notice that there are important sides in this question. If there is a heavy side, open a map besides vector<> to record whether this side has been added. If it has been added, do not add it again, which will affect the judgment. Note that the pair of map in the poj is initialized with a list {} will be CE, and make_pair is required.

 

The common practice of course is to recalculate the bridge every time an edge is added, but the complexity will reach O(q*M).

Then consider shrinking points. After shrinking points, it becomes a tree. If you don’t know how to connect and shrink points, go to the previous blog (talking about shrinking points)

 

We need to "dynamically" seek bridges on the basis of the original image.

It can be found that after turning into a tree, each edge on the tree is an edge connected component. When the newly connected edge is a point inside a connected component, the number of bridges remains unchanged. When connecting points on two connected components, you will find that a ring appears, and all the original edges on the ring will no longer be bridges.

 

How to find this ring?

We can find the LCA of u,v, and then the ring is v->LCA(u,v)->u>(u,v)->v.

How to shrink it? What I have always done before is "static shrink point", that is, the structure of the side-by-side graph remains unchanged. If we add edges dynamically, we have to modify all the points in the graph to be new contraction points every time, so it won't work directly. Did you find it very similar here and check it? Just traverse to lca once and pass all the loops and condense them into a new point. Each query is to check the points and collect the ancestors.

This is the new posture of this question: use union search to maintain the contraction point of dynamic addition

It turns out that the naive way to find LCA is to use level[] to represent the depth of each node. Two points climb to the root at the same time, and the deeper node climbs first (LCA(u,v) = LCA(u, father[v ]) ), when the depth is the same, the two points climb together (LCA(u, v) = LCA(father[u], father[v])) until the two points are the same. }The advantage of this method is that you can get the LCA while shrinking. Then we need a father[] to maintain the parent node of the node. In this way, the father[] can be directly used to check the collection and maintenance, and use it to indicate the contraction point and the BCC to which it belongs.

Here is the same way to find lca on tarjan, but it is more convenient, that is, use dfn to find lca. Through simulation, it is found that the original code for seeking lca through deep simplicity is as follows. Increase the depth one by one. The same method can be used to find lca with dfn. If the difference between dfn on both sides is huge, then the big dfn[x] in the search tree will eventually reach a small place, the smallest will reach the root node, otherwise it will reach their closer father Ancestor node. In short, the simulation discovery method is equivalent.

For simulation, you can directly use a tree diagram that is already connected components to simulate.

In this way, when we maintain the lca, we will use it by the way and check the number of continuously updated bridges and shrink the new ring to a point.

int lca(int a,int b){
    while(d[a] > d[b]){
        a = f[a];
    }
    while(a != b){
        a = f[a];
        b = f[b];
    }
    if(a == b){
        return a;
    }
}
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<stack>
#include<cmath>
#include<map>
#include<set>
#include<cstdio>
#include<algorithm>
#define debug(a) cout<<#a<<"="<<a<<endl;
using namespace std;
const int maxn=1e5+100;
typedef int LL;
vector<LL>g[maxn];
map<pair<LL,LL>,LL>ma;
LL n,m;
LL low[maxn],dfn[maxn],times=0,bridges=0,fa[maxn],pre[maxn];//fa为并查集,pre表示某点的父亲节点 
bool vis[maxn];//标记桥 
void init()
{
	for(LL i=0;i<=n;i++) fa[i]=i,g[i].clear();
	memset(pre,0,sizeof(pre));
	memset(vis,0,sizeof(vis));
	memset(low,0,sizeof(low));
	memset(dfn,0,sizeof(dfn));
	times=0;bridges=0;
	ma.clear();
}
LL find(LL x){ if(fa[x]==x) return fa[x]; return fa[x]=find(fa[x]);}
void unionn(LL a,LL b)
{
	LL x=find(a);LL y=find(b);
	if(x!=y) fa[x]=y;
}
void tarjan2(LL u)
{
    dfn[u]=low[u]=++times;
    for(LL i=0;i<g[u].size();i++){
        LL to=g[u][i];
        if(!dfn[to]){
        	pre[to]=u;
            tarjan2(to);
            if(low[to]<=dfn[u]){
            	unionn(u,to);
			}
            if(low[to]>dfn[u])
            {
                 vis[to]=vis[u]=true;
                 bridges++;
            }
            low[u]=min(low[u],low[to]);
        }
        else if(to!=pre[u])low[u]=min(low[u],dfn[to]);
    }

}
LL lca(LL x,LL y)
{
	if(dfn[x]<dfn[y]) swap(x,y);
	while(dfn[x]>dfn[y])
	{
		if(find(x)!=find(pre[x])){
			fa[find(x)]=find(pre[x]);bridges--;
		}
		x=pre[x];
	}
	while(x!=y)
	{
		if(find(y)!=find(pre[y])){
			fa[find(y)]=find(pre[y]);bridges--;
		}
		y=pre[y];	
	}
	
	return bridges;
}
int main(void)
{
  
  int cas=1;
  while(scanf("%d%d",&n,&m)&&(n+m))
  {
  	init();
  
  	for(LL i=1;i<=m;i++)
  	{
     	LL u,v;scanf("%d%d",&u,&v);
		if(!ma.count(make_pair(u,v))) g[u].push_back(v);g[v].push_back(u);
		ma[make_pair(u,v)]++;ma[make_pair(u,v)]++;
  	}
  	for(LL i=1;i<=n;i++){
  		
    	if(!dfn[i]) tarjan2(i);
  	}
  	LL Q;scanf("%d",&Q);
  	printf("Case %d:\n",cas++);    	
  	for(LL k=1;k<=Q;k++){
  		LL x,y;cin>>x>>y;
		cout<<lca(x,y)<<endl;	
	}
	printf("\n");
  }
  return 0;
}

 

Guess you like

Origin blog.csdn.net/zstuyyyyccccbbbb/article/details/108906344