[LCA][点双连通分量]压力

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/SSL_QYH0Ice/article/details/80962747

题目描述

如今,路由器和交换机构建起了互联网的骨架。处在互联网的骨干位置的核心路由器典型的要处理100Gbit/s的网络流量。他们每天都生活在巨大的压力之下。
小强建立了一个模型。这世界上有N个网络设备,他们之间有M个双向的链接。这个世界是连通的。在一段时间里,有Q个数据包要从一个网络设备发送到另一个网络设备。
一个网络设备承受的压力有多大呢?很显然,这取决于Q个数据包各自走的路径。不过,某些数据包无论走什么路径都不可避免的要通过某些网络设备。
你要计算:对每个网络设备,必须通过(包括起点、终点)他的数据包有多少个?

Input
第一行包含3个由空格隔开的正整数N,M,Q。
接下来M行,每行两个整数u,v,表示第u个网络设备(从1开始编号)和第v个网络设备之间有一个链接。u不会等于v。两个网络设备之间可能有多个链接。
接下来Q行,每行两个整数p,q,表示第p个网络设备向第q个网络设备发送了一个数据包。p不会等于q。

Output
输出N行,每行1个整数,表示必须通过某个网络设备的数据包的数量。

Sample Input
4 4 2
1 2
1 3
2 3
1 4
4 2
4 3

Sample Output
2
1
1
2

【样例解释】
设备1、2、3之间两两有链接,4只和1有链接。4想向2和3各发送一个数据包。显然,这两个数据包必须要经过它的起点、终点和1。

Data Constraint
对于40%的数据,N,M,Q≤2000
对于60%的数据,N,M,Q≤40000
对于100%的数据,N≤100000,M,Q≤200000

分析

这题要找出点对之间的割点,容易想到点双连通分量
然后用Tarjan求出来
差分做法:
LCA求公共祖先,差分预备让公共祖先-1,祖先父亲-1
然后后面叠上去
对于点双连通分量,我们将其缩成一个点,但不是全部消失,这个点用于沟通其他点,其他点之间的联系被割断,即在点双连通分量重产生一个割点,使差分时数值不出错

#include <iostream>
#include <cstdio>
#include <memory.h>
#include <algorithm>
#define rep(i,a,b) for (i=a;i<=b;i++)
const int N=300001;
using namespace std;
int n,m,q; 
struct Edge {
int u,v,nx;
}g[4*N],e[2*N];
int cnt=1,ent,list[2*N];
int low[N],dfn[N],dcnt,idcnt;
int stk[N],top;
int ans[N],s[N][19],d[N];

void Addedge(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
    g[++cnt].u=v;g[cnt].v=u;g[cnt].nx=list[v];list[v]=cnt;
}

void Tarjan(int u,int fl) {
    low[u]=dfn[u]=++dcnt;
    stk[++top]=u;
    for (int i=list[u];i;i=g[i].nx)
    if ((fl^1)!=i) {
        int j=g[i].v;
        if (!dfn[j]) {
            Tarjan(j,i);
            if (low[j]==dfn[u]) {
                idcnt++;
                for (;stk[top]!=j;top--) {
                    e[++ent].u=stk[top];
                    e[ent].v=idcnt+n;
                }
                e[++ent].u=j;
                e[ent].v=idcnt+n;
                e[++ent].u=u;
                e[ent].v=idcnt+n;
                top--;
            }
            if (low[j]==dfn[j]) {
                e[++ent].u=u;e[ent].v=j;
            }
            low[u]=min(low[u],low[j]);
        }
        else
        low[u]=min(low[u],dfn[j]);
    }
    if (low[u]==dfn[u]) top--;
}

void Lca_prepare(int u,int f) {
    s[u][0]=f;d[u]=d[f]+1;
    int i;
    rep(i,1,18) s[u][i]=s[s[u][i-1]][i-1];
    for (i=list[u];i;i=g[i].nx)
    if (g[i].v!=f) Lca_prepare(g[i].v,u);
}

int Lca(int a,int b) {
    if (d[a]<d[b]) swap(a,b);
    int i;
    for (i=18;i>=0;i--)
    if (d[s[a][i]]>=d[b])
    a=s[a][i];
    if (a==b) return a;
    for (i=18;i>=0;i--)
    if (s[a][i]!=s[b][i]) {
        a=s[a][i];
        b=s[b][i];
    }
    return s[a][0];
}

void Get(int u) {
    for (int i=list[u];i;i=g[i].nx)
    if (g[i].v!=s[u][0]) {
        Get(g[i].v);
        ans[u]+=ans[g[i].v];
    }
}

int main() {
    int i;
    scanf("%d%d%d",&n,&m,&q);
    rep(i,1,m) {
        int u,v;
        scanf("%d%d",&u,&v);
        Addedge(u,v);
    }
    Tarjan(1,0);
    memset(g,0,sizeof(g));memset(list,0,sizeof(list));
    cnt=0;
    rep(i,1,ent)
    Addedge(e[i].u,e[i].v);
    Lca_prepare(1,0);
    rep(i,1,q) {
        int u,v;
        scanf("%d%d",&u,&v);
        int l=Lca(u,v);
        ans[u]++;ans[v]++;ans[l]--;ans[s[l][0]]--;
    }
    Get(1);
    rep(i,1,n)
    printf("%d\n",ans[i]);
}
展开阅读全文

没有更多推荐了,返回首页