机房测试2:home(tarjan求必经点)

题目:

求一张无向图从1走到N的必经点

n<=2e5   m<=4e5

分析:

必经点意为:删除后整个图不连通,但不能直接求割点。因为删掉的那个点可以使图不连通,但不能保证1与n不连通。

所以要删的是在1到n路径上的点。也就是说,删掉的点能使1和n隔开。

法1:

tarjan判环的时候从1开始,并维护一个数组siz[v]:表示v这个点在搜索树中的子树里有没有n这个点,每一次回溯的时候将标记回传给u(u是v的father)。

在判割点的条件:low[v]<=dfn[u](u的子树不能到达比u高的点) 再加一个限制:siz[v]==1(v这个子树中有n)

为什么不是siz[u]==1?

可能出现u的非v子树中有n,去掉u后1和n仍相连。

法2:

判环的时候多加这一个条件:dfn[v]<=dfn[n](n比v后遍历到)

画图可知满足题意

#include<bits/stdc++.h>
using namespace std;
#define N 200005
#define ri register int
int siz[N],low[N],nex[N<<2],head[N],to[N<<2],dfn[N],cut[N];
int Ti=0,tot=0,n,m,ans=0;
int read()
{
    int x=0,fl=1; char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') fl=-1; ch=getchar(); }
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
    return x*fl;
}
void add(int a,int b)
{
    to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
    to[++tot]=a; nex[tot]=head[b]; head[b]=tot;
}
void dfs(int u,int fa)
{
    dfn[u]=low[u]=++Ti;
    if(u==n) siz[u]=1;
    bool flagg=0;
    for(ri i=head[u];i;i=nex[i]){
        int v=to[i];
        if(!dfn[v] && v!=fa){
            dfs(v,u);
            low[u]=min(low[u],low[v]);
            siz[u]|=siz[v];
            if(siz[v] && low[v]>=dfn[u]) flagg=1;
            //这里一定是siz[v] 为了保证一定是从v的子树传上来的标记 而不是从u的另一个子树传上来的标记
            //因为这里判u是割点是根据子树v来判断的
            //也就是说 割掉u之后 子树v和其它部分会分裂 而n刚好在子树v中 而不会和1在一个块中
            //还有一种打法:dfn[v]<=dfn[n](也就是说n在v后遍历到)
            //1. n在v的子树中  2.n在u的另一个非v的子树里(因为n一定要提前遍历 dfn才有值 所以正确性可以保证) 
        }
        else if(v!=fa) low[u]=min(low[u],dfn[v]);
    }
    if(u!=1 && flagg) ans++,cut[u]=1;
}
int main()
{
    //freopen("home.in","r",stdin);
    //freopen("home.out","w",stdout);
    int T=read();
    while(T--){
        n=read(); m=read();
        while(m--) add(read(),read());
        dfs(1,0);
        printf("%d\n",ans);
        for(ri i=1;i<=n;++i) if(cut[i]) printf("%d ",i);
        printf("\n");
        for(ri i=1;i<=n;++i) dfn[i]=nex[i]=to[i]=head[i]=low[i]=siz[i]=cut[i]=0; 
        Ti=0; tot=0; ans=0;
    }
}
/*
1
10 11
1 10
10 6
6 7
7 8
8 5
5 3
3 9
9 4
4 2
6 2
8 7
*/
View Code

 

转载于:https://www.cnblogs.com/mowanying/p/11620377.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值