Point Double Connectivity Component and Round Square Tree (Big Pit)

Catalog

Point Double Connected Component

Cutting point

Deleting this point disconnects the original map is called a cut point.
Generally, the point set that can disconnect the original map after deleting the point set is called the cut point set.
In this post, we will focus on the nature of the former.

Point Double Connected Subgraph and Point Double Connected Component

A subgraph without a cut point is called a point-connected subgraph.
A very large point double-connected subgraph is called a point double-connected component (generally referred to as point double without special explanation).
The point double-connected component must be a point double-connected subgraph, and the point double-connected subgraph is not necessarily a point double-connected component.

Properties of Point Double Connected Components

Let's start with a few very simple properties.See the next section for more informationRound Square Tree.

Internal Property (Property of Point Double Connected Component)

First, the point double-connected component inherits the properties of the point double-connected subgraph.

  • There are two simple paths between any pair of points in a point dual-connected component, making the union of the points they pass through an empty set.

This is easy to prove, because if there are no such two simple paths, consider the point through which the two simple paths must pass together, which is obviously also in point pairs, deleting it will disconnect this subgraph and contradict the original proposition.

External properties (the relationship between point double-connected components, secant points and undirected graphs)

The external properties of point pair are the unique properties of point pair connected components, such as (without detailed proof):

  • An undirected graph with a cut point can necessarily be composed of several points which are connected by two components.
  • The common point between the two connected components of a point must be unique and must be a secant point.
  • A cut point must be connected with at least two points that are double connected.

The application and detailed proof of these conclusions are discussed below.

The Simplest Application of Point Double Connectivity Component-Connectivity of Graph

You have an undirected graph on which you can set several key points so that after any point explodes, each connected block has at least one key point.You need to keep the number of key points as small as possible, output this minimum and the number of settings that match this minimum.
source:lp3225 Mine Setup

First, it is clear that the interconnection blocks will not affect each other.
Consider the connected blocks without cut points first.Obviously, each of these points explodes without affecting the number of connected blocks in the graph.So we can put any two key points in it, except one point in the connected block of only one point.
Then, the connected block with a cut point must be made up of several points which are connected by the cut points.Obviously, a point explosion in a point pair, which is not a cut point, has no effect on the connected block.
If this point has only one cut point connected to the outside world, it will become an isolated connection block after the cut point explodes, and a non-cut point must be selected to place the key point.
If this point double has two cut points connected to the outside world, then it does not need to place a key point, because it will inevitably be double-connected with the points related to the key points after the explosion (if you go along a cut point and go out from different cut points all the time, there will be no way to go, that is, you go to a point double with only one cut point, which also guarantees that in each connected block of the original diagramThere is at least one key point, which will be demonstrated when we talk about square trees below).



Round Square Tree

Introduction: Round Square Tree

We find that the cut point is the intersection of two points, and we have some conclusions:

  • There is only one common secant point between two adjacent points which are double connected components.

This conclusion can be proved by negation.If there are two common secant points in the dual-connected component of two points, even if one of them is deleted, the connectivity of the two points will not be changed. If this is the case, then the subgraph formed by the double-joining of the two points is also a point double-connected subgraph, which does not conform to the definition of the dual-connected component of points, and the original proposition is proved.

  • A simple path connecting two points within a point's double-connected component cannot pass through the points outside the point.

This conclusion is also well supported.If a simple path between two points inside a connection point passes through a point outside the point, it is clear that this point can be added to the point double.The original proposition is proved.
This conclusion tells us that point double must come back from a cut point.

A conclusion can also be derived from this:

  • For any pair of points in either pair of points, the union of the points through which the simple path connecting them must be the dual connected component of the point itself.

Proof: First of all, from the first conclusion, this union must be contained in the point set of the dual connected component of this point.Then it is proved that it contains the point set of the dual connected component of this point.Suppose one pair of points in two points((s,f) \) Can't pass a point \(c\) in a simple path, so consider the points that must be repeated along the road. Obviously, these points should also be in the middle of the two points. However, because of the nature of the two points, deleting this point will not disconnect the dual-connected components of the points, so this does not have to be repeated, which contradicts the original proposition, so it is proved.
This conclusion tells us that if a road crosses a square point on a square tree, it can also go through all the points to which the square belongs in a simple path on the original map.

Based on the second conclusion, we may wonder:

  • If each point is treated as a double point, the undirected graph will be a tree when these points are connected in a neighboring relationship.

But this is incorrect, because although a point double from a cut point must come back from this cut point, since this cut point may connect the third point double, we can reach the third point double through this cut point and repeat through this cut point to return to the original point double. Because the cut point is deleted from the graph, it is also a simple path.This creates a ring, so the picture is not a tree.However, based on the first conclusion that there is only one common point between two points, we can consider retaining the cut point so that it is impossible to find a simple path that has the same start and end points (i.e., loops).

Thus, an algorithm for reducing an undirected graph to a tree, a square tree, emerges as the times require.

What is a square tree?

A circular square tree is a tree formed by indenting undirected graph points based on the dual connected component of points.
We condense the double-connected component of each point into a "square point" and call the original point a "circle point".Then we delete all the edges of the original graph so that each point is connected to the "square point" of the point to which it belongs.Obviously, a cut point connects to more than one square point, so all non-cut points except the root node are leaf nodes.We also know that circles and points do not connect to each other, nor do squares and points connect to each other.

What can a square tree do?

As a data structure that accurately shows the relationship between two points in the original map, due to the excellent properties of point pairs about simple paths and connectivity, circular square trees can handle some problems about simple paths and connectivity.
It's good that it combines the properties of tree and point pair, and also preserves the information of the original map we need.Now let's talk about the application of square trees, and by the way, the various other properties of square trees.

The Classic Application of Round Square Tree--Statistical Simple Path

Example 1

You have an undirected graph with a weight at each point, modified and queried: modify the weight at any point, or count the point that all simple paths pass through between two points and focus the point with the smallest weight.
source: Codeforces 487E tourists

Consider that there may be many simple paths between two points, while most of the points they pass through are duplicates.Remember?We conclude that:

  • For any pair of points in either pair of points, the union of the points through which the simple path connecting them must be the dual connected component of the point itself.

So let's consider a point double as a unit.After building a graph with a square tree, the simple path in the graph is the simple path on the tree, because on the tree, the simple path between two points is unique.So we can maintain the minimum value of the pair of dots at each point. For each modification, we first deal with it violently, then run lca on the direct tree.

But!Things aren't that simple.
We know that a dot can connect to more than one square point, so if you modify it violently and the time complexity is $O (degrees*\log n)$then consider a graph like chrysanthemum and stamp the chrysanthemum with a large degree of modification, you will get good results with TLE.

So we can consider modifying only one dot's father's square point, which can effectively reduce the number of repeated statistics. If lca is a square point in statistics, remember to count its father's square point.This reduces the complexity to \(O(n \log n)\).

Example 2

You have an undirected graph that finds the tuple \((s,c,f)\) such that \(s,c,f\) is not equal and there is a simple path that satisfies the starting point \(s\), the ending point is \(f\), and the number of passes through \(c\).
source: APIO2018 Iron Man Items

First, we consider how many \(c\) satisfy the criteria for a fixed \((s,f)\).
Obviously, once a square tree is built, \(s,c\) contributes to the answer by all the dots on both sides of the point to which all the square points on the simple path of the tree belong.However, our starting point, ending point and cutting point are all counted once more.
We observed that all the starting points, ending points and cutting points are points on the path.So we set the weight for the point, the weight for the square point is set to \(size\) for both points, and the weight for the circle is set to \(-1\).The problem then translates into finding the weight of all the simple paths on a tree (the starting and ending points must be circles, and the starting and ending points can be adjusted).
We completed the problem and the transformation of the square tree.However, the statistical problem is still too complex (in time).So instead, we count the number of times each point has been traveled by a simple path (that is, the number of simple paths above that point)\(*\) the weight of that point.


So our complexity drops to \(O(n)\, and this problem is solved.
At the same time, attention should be paid to the details of statistical paths (as well as to dealing with forest problems).

A Classic Application of Round Square Trees--Connectivity of Graphs (Intermittent)

Let's first consider the properties of leaf nodes in a square tree.Remember the previoustopic Are you?

code implementation

Mine Setup:

#include<cstdio>
#include<vector>
#include<cstring>
const int MaN=20005;
const int MaE=100005;
struct edge{
    int to,ne;
}e[2*MaE];
int h[MaN],cnt;
void add(int a,int b){
    e[++cnt]=(edge){b,h[a]};
    h[a]=cnt; return ;
}

std::vector <int> bcc[MaN];
int num;
int low[MaN],dfn[MaN];
int st[MaN],con,dfnCnt;
bool iscut[MaN];
void tjw(int x,int fa){
    int child=0;
    dfn[x]=low[x]=++dfnCnt;
    st[con++]=x;
    for(int i=h[x];i;i=e[i].ne){
        int v=e[i].to;
        if(!dfn[v]){
            ++child;
            tjw(v,x);
            low[x]=std::min(low[v],low[x]);
            if(low[v]>=dfn[x]){
                iscut[x]=true;
                ++num;
                while(st[con]!=v)
                    bcc[num].push_back(st[--con]);
                bcc[num].push_back(x);
            }
        }
        else if(v!=fa) low[x]=std::min(low[x],dfn[v]);
    }
    if(!fa&&child<=1) iscut[x]=false;
    return ;
}

void init(){
    memset(h,0,sizeof h);
//	memset(e,0,sizeof e);
    memset(st,0,sizeof st);
    memset(dfn,0,sizeof dfn);
//	memset(low,0,sizeof low);
    memset(iscut,false,sizeof iscut);
    for(int i=1;i<=num;i++) bcc[i].clear();
    cnt=num=con=0;
}
int n,m;
int main(){
    scanf("%d",&m);
    int T=0;
    do{
        init();
        ++T;
        long long ans=1;
        n=0;
        for(int i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            add(a,b);add(b,a);
            n=std::max(n,std::max(a,b));
        }
        for(int i=1;i<=n;i++) if(!dfn[i]) tjw(i,0);
        int anss=0;
        for(int i=1;i<=num;i++){
            int lscnt=0;
            for(int j=0;j<bcc[i].size();j++)
                if(iscut[bcc[i][j]]) ++lscnt;
            if(!lscnt) ans*=bcc[i].size()*(bcc[i].size()-1ll)/2,anss+=2;
            if(lscnt==1) ans*=(bcc[i].size()-1),anss+=1;
        }
        for(int i=1;i<=n;i++) if(!h[i]) ++anss;
        printf("Case %d: %d %lld\n",T,anss,ans);
        scanf("%d",&m);
    }while(m!=0);
    return 0;
}

[Tourists] (#Example 1 "Tourists")

#include<cstdio>
#include<queue>
#include<set>
#include<cstdlib>
#include<cstring>
const int MaN=2e5,MaE=5e5;
class edgeSet{
    public:
    edgeSet(){memset(this,0,sizeof *this);}
    struct edge{int to,ne;}e[MaE];
    int h[MaN],cnt;
    void add(int a,int b){
        e[++cnt]=(edge){b,h[a]};
        h[a]=cnt; return ;
    }
}e1,e2;

int w[MaN];
std::multiset<int> s[MaN];
int n,m,q;
int st[MaN],dfn[MaN],low[MaN];
//std::priority_queue <int,vector<int>,greater<int> > q[MaN];

int dfncnt,bcccnt,con;
void tjw(int x,int fa){
    st[con++]=x;
    low[x]=dfn[x]=++dfncnt;
    #define e e1.e
    #define h e1.h
    for(int i=h[x];i;i=e[i].ne){
        if(e[i].to==fa) continue;
        int v=e[i].to;
        if(!dfn[v]){
            tjw(v,x);
            low[x]=std::min(low[v],low[x]);
            if(low[v]>=dfn[x]){
                ++bcccnt;
                while(st[con]!=v){
                    int now=st[--con];
                    e2.add(now,bcccnt);
                    e2.add(bcccnt,now);
                    s[bcccnt].insert(w[now]);
                }
                e2.add(x,bcccnt);
                e2.add(bcccnt,x);
                s[bcccnt].insert(w[x]);
            }
        }
        else low[x]=std::min(low[x],dfn[v]);
    }
    #undef e
    #undef h
    return ;
}

int son[MaN],zh[MaN],top[MaN],fat[MaN],depth[MaN];
void dfs(int x,int fa){
//	dfn[x]=++dfncnt;
    fat[x]=fa;
    if(x>n) s[x].erase(s[x].find(w[fa]));
    #define e e2.e
    #define h e2.h
    for(int i=h[x];i;i=e[i].ne){
        if(e[i].to==fa) continue;
        depth[e[i].to]=depth[x]+1;
        dfs(e[i].to,x);
        son[x]+=son[e[i].to]+1;
        zh[x]=son[zh[x]]>son[e[i].to]?zh[x]:e[i].to;
    }
    #undef e
    #undef h
    return ;
}
void rdfs(int x,int fa){
    dfn[x]=++dfncnt;
    #define e e2.e
    #define h e2.h
    top[zh[x]]=top[x];
    if(zh[x]) rdfs(zh[x],x);
    
    for(int i=h[x];i;i=e[i].ne){
        if(e[i].to==fa||e[i].to==zh[x]) continue;
        top[e[i].to]=e[i].to; rdfs(e[i].to,x);
    }
    
    #undef e
    #undef h
}

int vals[MaN<<2];
#define mid ((l+r)>>1)
void ins(int k,int l,int r,int pos,int val){
    if(l==r){
        vals[k]=val;
        return ;
    }
    if(pos<=mid) ins(k<<1,l,mid,pos,val);
    else ins(k<<1|1,mid+1,r,pos,val);
    vals[k]=std::min(vals[k<<1],vals[k<<1|1]);
    return ;
}
int que(int k,int l,int r,int ql,int qr){
//	printf("%d %d\n",ql,qr);
    if(l==ql&&r==qr) return vals[k];
    int ans=0x3f3f3f3f;
    if(ql<=mid) ans=std::min(que(k<<1,l,mid,ql,std::min(mid,qr)),ans);
    if(qr>mid) ans=std::min(que(k<<1|1,mid+1,r,std::max(ql,mid+1),qr),ans);
    return ans;
}

#undef mid

void update(int x,int y){
    ins(1,1,bcccnt,dfn[x],y);
    if(fat[x]!=x){
        s[fat[x]].erase(s[fat[x]].find(w[x]));
//		printf("%d\n",*(s[fat[x]].begin()));
        s[fat[x]].insert(y);
//		printf("%d\n",*(s[fat[x]].begin()));
        ins(1,1,bcccnt,dfn[fat[x]],*(s[fat[x]].begin()));
    }
    w[x]=y;
    return ;
}
int query(int a,int b){
    int ans=0x3f3f3f3f;
    while(true){
        int anca=top[a],ancb=top[b];
        if(anca==ancb){
            ans=std::min(ans,dfn[a]>dfn[b]?que(1,1,bcccnt,dfn[b],dfn[a]):que(1,1,bcccnt,dfn[a],dfn[b]));
            a=b=(dfn[a]>dfn[b]?b:a);
            break;
        }
        if(depth[anca]<=depth[ancb]){
            ans=std::min(ans,que(1,1,bcccnt,dfn[ancb],dfn[b]));
            b=fat[ancb];
        }
        else{
            ans=std::min(ans,que(1,1,bcccnt,dfn[anca],dfn[a]));
            a=fat[anca];
        }
    }
    if(a>n) ans=std::min(ans,w[fat[a]]);
    return ans;
}

bool flag=true;
void init(){
    scanf("%d%d%d",&n,&m,&q);
    bcccnt=n;
    for(int i=1;i<=n;i++){
        scanf("%d",&w[i]);
        if(w[i]!=1) flag=false;
    }
    for(int i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        e1.add(a,b),e1.add(b,a);
    }
    top[1]=1;
}
int main(){
    init();
    tjw(1,0);
    memset(dfn,0,sizeof dfn);
    dfncnt=0;
    dfs(1,1);
    rdfs(1,1);
    for(int i=1;i<=n;i++) s[i].insert(w[i]),ins(1,1,bcccnt,dfn[i],w[i]);
    for(int i=1;i<=bcccnt;i++){
        int now=*(s[i].begin());
        ins(1,1,bcccnt,dfn[i],now);
    }
    char opt;
    int a,b;
    for(int i=1;i<=q;i++){
        opt=getchar();
        while(opt!='C'&&opt!='A') opt=getchar();
        if(opt=='A'){
            scanf("%d%d",&a,&b);
            printf("%d\n",query(a,b));
        }
        else{
            scanf("%d%d",&a,&b);
            update(a,b);
        }
    }
    return 0;
}

Duathlon

#include<cstdio>
#include<algorithm>
#include<cstring>
class edgeSet{
    struct edge{int to,ne;}e[500005];
    int h[200005],cnt,now;
    public:
        edgeSet(){memset(this,0,sizeof(edgeSet));}
        void add(int a,int b){
            e[++cnt]=(edge){b,h[a]};
            h[a]=cnt; return ;
        }
        int getV(){return e[now].to;}
        int setBeg(int x){return now=h[x];}
        int setNxt(int i){return now=e[i].ne;}
}e1,e2;

int bcccnt,dfncnt;
int dfn[200005],low[200005];
int st[200005],con;
int val[200005];

int siz[200005],cols[200005];
void tjw(int x,int fa,int col){
    cols[x]=col; ++siz[col];
    dfn[x]=low[x]=++dfncnt;
    st[con++]=x;
    for(int i=e1.setBeg(x);i;i=e1.setNxt(i)){
        if(e1.getV()==fa) continue;
        int v=e1.getV();
        if(!dfn[v]){
            tjw(v,x,col);
            low[x]=std::min(low[x],low[v]);
            if(low[v]>=dfn[x]){
                ++bcccnt;
                cols[bcccnt]=col;
                while(st[con]!=v){
                    int now=st[--con];
                    e2.add(bcccnt,now);
                    e2.add(now,bcccnt);
                    ++val[bcccnt];
                }
                e2.add(x,bcccnt);
                e2.add(bcccnt,x);
                ++val[bcccnt];
            }
        }
        else low[x]=std::min(low[x],dfn[v]);
    }
    return ;
}

int n,m;
int son[200005];
long long ans[200005];
void dfs(int x,int fa){
    dfn[x]=1;
    ans[x]=(siz[cols[x]])*1ll*(siz[cols[x]]-1ll)/2ll;
//	printf("%d :\n",x);
    for(int i=e2.setBeg(x);i;i=e2.setNxt(i)){
        if(e2.getV()==fa) continue;
        int v=e2.getV();
//		printf("%d\n",v);
        dfs(v,x);
        son[x]+=son[v]+(v<=n);
        ans[x]-=(son[v]*1ll+(long long)(v<=n))*(son[v]*1ll-(long long)(v>n))/2ll;
    }
    ans[x]-=(siz[cols[x]]-son[x]-1ll+(long long)(x>n))*(siz[cols[x]]-son[x]-2ll+(long long)(x>n))/2ll;
    ans[x]*=val[x];
//	printf("%d,%d\n",son[x],fa);
    return ;
}
//Do not mix up the number of square tree points with the number of original map points.
int main(){
    int colcnt=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) val[i]=-1;
    bcccnt=n;
    for(int i=1;i<=m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        e1.add(a,b); e1.add(b,a);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i]) tjw(i,0,++colcnt);
    memset(dfn,0,sizeof dfn);
    for(int i=1;i<=n;i++)
        if(!dfn[i]) dfs(i,0);
    long long nowans=0;
    for(int i=1;i<=bcccnt;i++) nowans+=ans[i];
    
    printf("%lld\n",nowans*2ll);
    return 0;
}

Posted on Thu, 25 Jun 2020 20:07:26 -0400 by sgoku01