做题记录整理图论/tarjan P5058 [ZJOI2004]嗅探器(2022/10/19)

P5058 [ZJOI2004]嗅探器

首先,我们应该马上发现它求的和割点非常像,但是是对于两个点而言的割点

这时候就需要对tarjan有着比较深入的理解(也可能是我太拉了)

如果我们以其中一个点为起点做tarjan,我们在返回到路径上的一个节点时,此时该点的dfn序(简称x点)与另一个要求点(简称ed点)的dfn序就只有三种情况

dfn(x)<=dfn(ed)

dfn(ed)=0

dfn(x)>dfn(ed)

第一种情况就代表ed是x点的子树上的一个节点所以此时x点肯定是答案中的一个
第一种情况就代表ed还没访问到
第一种情况就代表ed在访问x之前就已经被访问过了
一遍tarjan就可以找出所有的答案,再输出字典序最小的那个就得了


 
 #include<bits/stdc++.h>
#define for1(i,a,b) for(int i = a;i<=b;i++)
#define mp(a,b) make_pair(a,b)
using namespace std;
struct node{
    int from;
    int to;
    int nex;
}a[500005*3];
int hd[500005],cnt;
int n,m,cut[500005],dfn[500005],low[5000005],t,ans,st,ed;
//dfn时间戳,low遇到的最小的时间戳,t为时间,cut[i]记录节点 i 是否为割点

void ru(int x,int y)
{
    a[++cnt].from=x;
    a[cnt].to=y;
    a[cnt].nex=hd[x];
    hd[x]=cnt;
}

void tarjan(int x)//fa为祖先
{
    dfn[x]=low[x]=++t;
    int kid=0;//以 fa 这个根节点的子树个数
    for(int i = hd[x];i;i=a[i].nex)
    {
        int v=a[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x]&&x!=st && dfn[ed]>=dfn[v]) //x并不是根节点同时 x 的祖先与 v只能通过 x 相连,所以x 可以是割点
                cut[x]=1;
            if(x==st) kid++;//如果枚举的节点是根节点,子树个数++,因为这个节点还没有被访问过,所以如果想到达他就需要经过 fa 节点
        }
        low[x]=min(low[x],dfn[v]);//易错点,直接硬背(好像无向图都是要这样?)
    }
//    if(kid>1&&x==fa) cut[x]=1;//如果根节点有两个以上的子树,那么他就应当是割点
}//判定一个点是否是根节点有两种情况,一种是发现low[son]>=dfn[fa]同时不是枚举的根节点
//另一种是根节点有了两个以上的子树
int main()
{
    int x,y;
    scanf("%d",&n);
    while(1)
    {
        scanf("%d%d",&x,&y);
        if(x==0&y==0) break;
        ru(x,y);
        ru(y,x);
    }
    scanf("%d%d",&st,&ed);
    for1(i,1,n)
    tarjan(st);
    for1(i,1,n)
    {
        if(cut[i]) 
        {
        	printf("%d",i);
        	return 0;
		}
	}
	puts("No solution");
    return 0;
}
posted @ 2022-10-20 08:10  yyx525jia  阅读(24)  评论(0)    收藏  举报