边双缩点

例题:加油站 NKOJ3082

某省有N座城市,这些城市间通过M条双向道路连接起来,使得任意两个城市都可相互到达。
每个城市都有两种车,一种使用柴油,一种使用汽油。
知名企业家何老板在每座城市都开设了一个加油站,有的加油站只卖汽油,有的加油站只卖柴油,有的加油站两种油都卖。
最近下了很长一段时间的暴雨,道路有被山洪冲毁的危险。
何老板很担心,他想知道,哪些道路毁坏了会使得某个或某些城市的车辆无法加到合适的油。
请你计算这样的道路总共有多少条?(两个城市间最多有一条道路直接相连)

简单分析一下

将该图边双缩点后,在同一大点的两点一定可以互相到达,也就是说同一大点内的节点可以相互提供汽油和柴油。

我们看大点与大点间的关系。

由于缩了点,图变成了树,于是树上的边一旦删去肯定会把图分成若干个连通块,连通块内部可以互相满足,但不能向外提供服务或者得到服务,就很容易判断一条边能不能断。

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=2e5+5;
int tt=1,las[N],ed[N],nt[N];
inline void add(int x,int y){ed[++tt]=y;nt[tt]=las[x];las[x]=tt;}
int dfn[N],low[N],total;
int bridge[N],scc,bel[N];
inline void tarjan(int x,int pre)
{
    dfn[x]=low[x]=++total;
    for(re i=las[x];i;i=nt[i])
    {
        int v=ed[i];
        if(!dfn[v])
        {
            tarjan(v,i);
            low[x]=min(low[x],low[v]);
            if(low[v]>dfn[x])bridge[i]=bridge[i^1]=1;
        }
        else if(i!=(pre^1)&&dfn[v]<dfn[x])low[x]=min(low[x],dfn[v]);
    }
}
int a[N],b[N],A[N],B[N];
inline void dfs(int x,int col)
{
    bel[x]=col;
    A[col]|=a[x];B[col]|=b[x];
    for(re i=las[x];i;i=nt[i])
    if(!bridge[i]&&!bel[ed[i]])dfs(ed[i],col);
}
vector<int>G[N];
int sz1[N],sz2[N],totA,totB,dep[N];
inline void DFS(int x,int fa)
{
    dep[x]=dep[fa]+1;
    sz1[x]=A[x];sz2[x]=B[x];
    for(re v:G[x])
    if(v!=fa)
    {
        DFS(v,x);
        sz1[x]+=sz1[v];
        sz2[x]+=sz2[v];
    }
}
signed main()
{
    int n,m,a1,b1;
    scanf("%d%d%d%d",&n,&m,&a1,&b1);
    while(a1--){int x;scanf("%d",&x);a[x]=1;}
    while(b1--){int x;scanf("%d",&x);b[x]=1;}
    while(m--){int x,y;scanf("%d%d",&x,&y);add(x,y);add(y,x);}
    tarjan(1,-1);
    for(re i=1;i<=n;++i)if(!bel[i])scc++,dfs(i,scc);
    for(re i=2;i<=tt;i+=2)
    if(bridge[i])
    {
        int x=ed[i],y=ed[i^1];
        x=bel[x];y=bel[y];
        G[x].push_back(y);
        G[y].push_back(x);
    }
    for(re i=1;i<=scc;++i)totA+=A[i],totB+=B[i];
    DFS(1,0);
    int ans=0;
    for(re i=2;i<=tt;i+=2)
    if(bridge[i])
    {
        int x=ed[i],y=ed[i^1];
        x=bel[x];y=bel[y];
        if(dep[x]>dep[y])swap(x,y);
        if(sz1[y]==0||sz2[y]==0){ans++;continue;}//y子树内挂掉
        if(totA==sz1[y]||totB==sz2[y]){ans++;continue;}//y子树外挂掉
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2021-10-08 16:56  kzsn  阅读(134)  评论(0编辑  收藏  举报