【JZOJ3325】【BJOI2013 load压力】(广义圆方树+LCA+树上差分)

Problem

  给定一个N(≤100000)个点、M(≤200000)条边的连通图,以及Q(≤200000)个点对(u,v)。求删除每个点后分别会有多少个点对不连通。

Solution

  • 考虑求出每个点对(u,v)的(搜索树)路径中,会造成影响的点。
  • 这看似就是u到v路径上的割点,实则不是。例如下图:
    这里写图片描述
  • 个中4到2有一条返祖边。
  • 设有个点对为(2,5),该路径上的点3为割点。因为删除点3后,以点7为根的子树“遂与外人间隔”。但它并不会影响到点2和点5的连通性。

  • 先思考一下割点的定义。
  • 删去某个割点后,会使全图的连通块数量增加。换句话说,会让它的某棵子树无法到达上面的连通块。
  • 而我们又知道,割点是被包含在2个或以上的点双里面的。确切地说,点双就是以割点为分界的;我们由某个点双到达另一个点双,也必须要经过某个割点。
  • 譬如对于下图:
    这里写图片描述
  • 一个红圈内包含一个点双,故点双有3个:{1,2},{1,3,4},{2,5,6}。个中,割点有2个:1、2。

  • 可以考虑将每个点双缩成一个点。准确地说,对于每个点双,我们都建一个方点(虚点);每个方点连向该点双内的所有点,后者成为圆点。换句话说,圆点就是原图中的点;方点就是新建的点。
  • 如下图所示:
    这里写图片描述
  • 由上图可知,方点由割点连接。同时,整个图被转化成了一棵树。这棵树就叫圆方树
  • 在圆方树中,u到v的路径对应原图中u到v必须经过的点。

  • 回到题目,对于点对(u,v),我们应在圆方树上的u到v的路径上给每个点的答案+1。因为这条路径其实包括了:1)起点u;2)终点v;3)必须经过的点双对应的方点;4)所有会影响到(u,v)连通性的割点。其中,1)2)4)都是我们必须维护的。
  • 显然,不能 O ( n ) 暴力维护。

这里写图片描述

  • 考虑上图:圆方树以点1为根;记w=lca(u,v)。我们现在要使u到v的路径答案+1。
  • 可以考虑树上差分。在点u打个+1标记,点v打个+1标记;点w打个-1标记,点father[w]打个-1标记。
  • 这样, O ( n ) dfs一遍即可求得答案。

  • 时间复杂度: O ( m + q + n α ( n ) ) t a r j a n L C A o r   O ( m + q + n l o g 2 n ) L C A

Code

#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#define min(a,b) ((a)<(b)?(a):(b))
#define fo(i,a,b) for(i=a;i<=b;i++)
#define repe(i,x) for (edge *i=x; i; i=i->ne)
using namespace std;

const int N=2e5+1,M=N<<2;
int i,j,n,m,Q,u,v;
struct edge
{
    int v;
    edge *ne;
}e[M],*tp=e,a[M],*tot=a,*final[N],*fin[N];

void read(int&x)
{
    char ch=getchar(); x=0;
    for(;!isdigit(ch);ch=getchar());
    for(;isdigit(ch);x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
}
inline void link(int u,int v)
{
    *tp=(edge){v,final[u]},final[u]=tp++;
    *tp=(edge){u,final[v]},final[v]=tp++;
}

inline void Link(int u,int v)
{
    *tot=(edge){v,fin[u]},fin[u]=tot++;
    *tot=(edge){u,fin[v]},fin[v]=tot++;
}

int tim,dfn[N],low[N],fat[N],top,sq;
edge *st[N];
void tarjan(int x)
{
    dfn[x]=low[x]=++tim;
    repe(i,final[x])
    {
        int y=i->v;
        if(y!=fat[x])
            if(!dfn[y])
            {
                fat[y]=x;
                st[++top]=i; tarjan(y);
                low[x]=min(low[x],low[y]);
                if(low[y]>=dfn[x])
                {
                    Link(n+(++sq),x);
                    do
                        Link(n+sq,st[top]->v);
                    while(st[top--]!=i);
                }
            }
            else    low[x]=min(low[x],dfn[y]);
    }

}

int p[N][2],sum[N],fa[N],w;
bool bz[N];
vector<int>ve[N][2];
vector<int>::iterator it;
int gef(int x)
{
    return fa[x]?fa[x]=gef(fa[x]):x;
}
void dfs(int x)
{
    int y;
    fo(y,0,1)
        for(it=ve[x][y].begin();it!=ve[x][y].end();it++)
            if(!bz[*it])
                    bz[*it]=1;
            else    w=gef(p[*it][!y]), sum[w]--, sum[fat[w]]--;

    repe(i,fin[x])
    {
        y=i->v;
        if(y!=fat[x]) fat[y]=x, dfs(y), sum[x]+=sum[y];
    }

    fa[x]=fat[x];
}

int main()
{
    read(n);read(m);read(Q);
    fo(i,1,m) read(u), read(v), link(u,v); 

    fo(i,1,n) if(!dfn[i]) tarjan(i);

    fo(i,1,Q)
        fo(j,0,1)
            read(p[i][j]), sum[p[i][j]]++, ve[p[i][j]][j].push_back(i);
    memset(fat,0,sizeof fat);
    dfs(1);

    fo(i,1,n) printf("%d\n",sum[i]);
}

猜你喜欢

转载自blog.csdn.net/qq_36551189/article/details/80979839
今日推荐