luogu题解 P3388 【【模板】割点(割顶)】

外加定义:在一个无向图中,如果删掉点 x 后图的连通块数量增加,则称点 x 为图的割点。

外加图示

开始思路为割桥上的点为割点,后来证明的确正确。
不过可惜的是他的逆定理错了(gg了),不过数据很弱以至于得了90分。

如图所示

图中无割桥,但点3却是割点,貌似无法解决。

(顺及客串my blog
,以及图论的必要工具

回归正题,另一种思路诞生了:
如果u点的子节点为v,v点他能返回的最老祖先比u点年轻或一样(即dfn[u]值<=low[v]),那么如果删去u点,那么v以下的点就会与v以上的点失去联系,就会产生新的连通块(实质是在我的原来思路上多了一个判断

也就是说如果在我们的搜索树上有一个点只有树边与祖先相连,而没有反向边连回祖先节点的话,那么它就是割点。就是没有这样的边

至于实现方法貌似蒟蒻所知只有Tarjan。

这道题是模板题,大家还是不要抄代码为好。(事关今后的Tarjan生涯)

代码

#include<cstdio>
#include<algorithm>
#include<stack>
#include<cstring>
#define Max 1000000+199
using namespace std;
int n,m,dfn[Max]={0},low[Max],cast[Max],ins[Max],inx=0,head[Max],v[Max]={0},cnt=0,gs=0,cd[Max]={0};
stack<int> s;
struct edge
{
    int c,to,next;
}e[Max];
void adde(int a,int b)
{
    cnt++;
    e[cnt].to=b;
    e[cnt].c=a;
    e[cnt].next=head[a];
    head[a]=cnt;
    cd[a]++;
}
int ans=0,gd[Max]={0};
void tarjan(int x,int fa)
{
    int u,sk=0;
    inx++;
    dfn[x]=low[x]=inx;
    s.push(x);
    ins[x]=1;
    for(int i=head[x];~i;i=e[i].next)
    {
        u=e[i].to;
        if(dfn[u]==0)
        {
            tarjan(u,fa);
            if(low[u]>=dfn[x]&&x!=fa)gd[x]=1;
            v[i]=v[i%2==0?i-1:i+1]=1;
            low[x]=min(low[x],low[u]);
            if(x==fa)sk++;
        }
        else if(ins[u]==1&&v[i]==0)v[i]=v[i%2==0?i-1:i+1]=1,low[x]=min(low[x],dfn[u]);
    }
    if(dfn[x]==low[x])
    {
        gs++;
        u=Max;
        while(u!=x)
        {
            u=s.top();
            s.pop();
            ins[u]=0;
            cast[u]=gs;
            //printf("%d %d\n",u,gs);
        }

    }
    if(x==fa&&sk>=2)gd[x]=1;
}
int main()
{
    memset(cd,0,sizeof(cd));
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        adde(a,b);
        adde(b,a);
        //printf("%d",v[i]);
    }
    for(int i=1;i<=n;i++)
    if(dfn[i]==0)tarjan(i,i);
    for(int i=1;i<=n;i++)
    {
        if(gd[i]==1)ans++;
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;i++)
    {
        if(gd[i]==1)printf("%d ",i);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zwp2004/p/10346599.html

时间: 2024-11-13 07:53:43

luogu题解 P3388 【【模板】割点(割顶)】的相关文章

洛谷 P3388 【模板】割点(割顶)

P3388 [模板]割点(割顶) 题目背景 割点 题目描述 给出一个n个点,m条边的无向图,求图的割点. 输入输出格式 输入格式: 第一行输入n,m 下面m行每行输入x,y表示x到y有一条边 输出格式: 第一行输出割点个数 第二行按照节点编号从小到大输出节点,用空格隔开 输入输出样例 输入样例#1: 6 7 1 2 1 3 1 4 2 5 3 5 4 5 5 6 输出样例#1: 1 5 说明 n,m均为100000 tarjan 图不一定联通!!!. 没错我以前就是不会,不服bb别solo #i

图论算法-Tarjan模板 【缩点;割顶;双连通分量】

图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; bool ins[100010]; int col[100010];//记录每个点所属强连通分量(即染色) vector<int> map[100010]; stack<int> st; int tot;//时间戳 int colnum;//记录强连通分量个数 void tarjan(

找割顶的模板

用邻接矩阵写的.自己慢慢理解吧 #include <iostream> #include <cstring> using namespace std; #define CC(c) memset(c, 0, sizeof(c)) const int maxn=5000; int iscut[maxn], g[maxn][maxn], low[maxn], pre[maxn], _pre, n, m; int dfs(int u, int fa) { low[u]=pre[u]=++_

【模板】无向图的割顶

无向图的割顶: Vector <int> G[] :邻接表存图 Int pre[] :存储时间戳 Int low[] : u及其后代所能连回的最早的祖先的pre值 Int iscut[] : =true表示是割顶,=false不是割顶 Dfs函数在主函数调用时,fa预设为-1. vector <int> G[MAXN]; int pre[MAXN],iscut[MAXN],low[MAXN],dfs_clock; int dfs(int u,int fa) { int lowu=p

无向图的割顶和桥,无向图的双连通分量入门详解及模板 -----「转载」

https://blog.csdn.net/stillxjy/article/details/70176689 割顶和桥:对于无向图G,如果删除某个节点u后,连通分量数目增加,则称u为图的割顶:如果删除某条边后,连通分量数目增加,则称该边为图的桥.对于连通图删除割顶或桥后都会使得图不再连通 以下我,我们利用dfs的性质来快速找出一个连通图中的所有的割顶和桥 首先我们要引入”时间戳”这个概念: 时间戳:表示在进行dfs时,每个节点被访问的先后顺序.每个节点会被标记两次,分别用pre[],和post

UVA 315 :Network (无向图求割顶)

题目链接 题意:求所给无向图中一共有多少个割顶 用的lrj训练指南P314的模板 #include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=109; struct Edge { int to,next; Edge(){} Edge(int _to,int _next) { to=_to; next=_next; } }edge[N*N*2]; int head[N]; int dfn[N],lo

codeforces22c System Administrator【给定一个割顶输出边 BCC】

Description Bob got a job as a system administrator in X corporation. His first task was to connectn servers with the help ofm two-way direct connection so that it becomes possible to transmit data from one server to any other server via these connec

无向图的割顶(poj1523,1144)

割顶:表示无向图中的点,这个点删除之后,原图不在联通,这样的点就是割顶. 怎么求一个图中的割顶呢? 把无向图变成一颗树,dfs时候搜索到在dfs树上的称为树边,搜索是出现后代指向祖先的边称为反向边. 对于根节点,当他存在两个或两个以上的子节点时,那么他就是割顶. 而对于其他节点u,当且仅当u存在一个子节点v,使得v及其所有的后代都没有反向边连回u的祖先时,u是一个割顶. 那么判断就很简单,这里给出两个模板: 题目:poj1523 和 1144都是裸的求割顶的题目 通用模板: #include <

连通分量 无向图的割顶和桥 无向图的双连通分量 有向图的强连通分量

时间戳 dfs_clock :说白了就是记录下访问每个结点的次序.假设我们用 pre 保存,那么如果 pre[u] > pre[v], 那么就可以知道先访问的 v ,后访问的 u . 现在给定一条边, (u, v), 且 u 的祖先为 fa, 如果有 pre[v] < pre[u] && v != fa, 那么 (u, v) 为一条反向边. 1 求连通分量: 相互可达的节点称为一个连通分量: #include <iostream> #include <cstd