HDU - 3594 Cactus(仙人掌图)

题目大意:给出仙人掌图的定义:

1.必须是强连通

2.每条边只能属于一个环

解题思路:在tarjan算法中加入点东西就可以判断了

只要该点能连到之前的点,那么形成环了,找到这个环的所有的边,并标记

如果有一条边被标记了两次了,那图就不是仙人掌图了

关键是怎么找到这个环的所有边,我们可以引入另一个栈,这个栈存放的是边的序号

假设当前点为u,u点连回之前的点是v,那么就从栈里面找边,找到出发点为v的边为止,找到的这些边都是环上的边,这个和tarjan算法的找同一个连通分量的点的道理是一样

#include <cstdio>
#include <cstring>
#pragma comment(linker, "/STACK:102400000,102400000")
#define N 20010
#define M 50010
#define min(a,b) ((a) < (b) ? (a) : (b))

struct Edge{
    int from, to, next, id;
}E[M];

int head[N], pre[N], lowlink[N], sccno[N], stack[N], stack2[M];
int tot, n, scc_cnt, dfs_clock, top, top2;
int queue[M];
bool vis[M];
bool flag;

void AddEdge(int from, int to) {
    E[tot].from = from;
    E[tot].to = to;
    E[tot].next = head[from];
    E[tot].id = tot;
    head[from] = tot++;
}

void init(){
    scanf("%d", &n);
    memset(head, -1, sizeof(head));
    tot = 0;

    int u, v;
    while (scanf("%d%d", &u, &v) && u + v) {
        AddEdge(u, v);
    }
}

void dfs(int u) {
    pre[u] = lowlink[u] = ++dfs_clock;
    stack[++top] = u;

    for (int i = head[u]; i != -1; i = E[i].next) {
        int v = E[i].to;
        stack2[++top2] = E[i].id;
        if (!pre[v]) {
            dfs(v);
            lowlink[u] = min(lowlink[u], lowlink[v]);
        }
        else if(!sccno[v]) {
            lowlink[u] = min(lowlink[u], pre[v]);
            int tmp = top2;
            while (1){
                int id = stack2[top2--];
                if (vis[id])
                    flag = true;
                vis[id] = true;
                if (E[id].from == v)
                    break;
            }
            top2 = tmp;
        }
        top2--;
    }

    int x;
    if (pre[u] == lowlink[u]) {
        scc_cnt++;
        while (1) {
            x = stack[top--];
            sccno[x] = scc_cnt;
            if (x == u)
                break;
        }
    }
}

void solve() {
    memset(pre, 0, sizeof(pre));
    memset(sccno, 0, sizeof(sccno));
    memset(vis, 0, sizeof(vis));
    dfs_clock = top = top2 = scc_cnt = 0;
    flag = false;
    dfs(0);

    for (int i = 0; i < n; i++) {
        if (sccno[i] != 1) {
            printf("NO\n");
            return ;
        }
    }

    if (flag)
        printf("NO\n");

    else
        printf("YES\n");
}

int main() {
    int test;
    scanf("%d", &test);
    while (test--) {
        init();
        solve();
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-15 06:01:50

HDU - 3594 Cactus(仙人掌图)的相关文章

bzoj 1023: [SHOI2008]cactus仙人掌图 tarjan索环&amp;&amp;环上单调队列

1023: [SHOI2008]cactus仙人掌图 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1141  Solved: 435[Submit][Status] Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路

【BZOJ 1023】 [SHOI2008]cactus仙人掌图

1023: [SHOI2008]cactus仙人掌图 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 1235  Solved: 482 [Submit][Status] Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是--注意到它有三条简单

【BZOJ】【1023】【SHOI2008】cactus仙人掌图

DP/仙人掌 题解:http://hzwer.com/4645.html->http://z55250825.blog.163.com/blog/static/150230809201412793151890/ QAQ了 呃……第一次做仙人掌的题目……感觉性质还是蛮神奇的(我是不是应该先做一点环套树的题目呢?>_>) 每个点都只会在一个简单环上,所以在dfs的时候,对于一个环,它上面的点是深度连续的一段(沿着father可以遍历这个环!),然后最后一个点再指回起始点,所以只要low改变了

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using

[树形dp][Tarjan][单调队列] Bzoj 1023 cactus仙人掌图

Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌 图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6 ,5,4).(7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两 个的简单回路里.另外,第三张图也不是仙人图,因为它并不是连通图

HDU 3594 Cactus(仙人掌问题)

http://acm.hdu.edu.cn/showproblem.php?pid=3594 题意: 一个有向图,判断是否强连通和每条边只在一个环中. 思路: 仙人掌问题. 用Tarjan算法判断强连通分量的时候,记录每节结点的父节点.当找到一个环后,回溯将该环上的所有结点+1,如果有结点出现2次了,则说明不是仙人掌了. 1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include&l

hdu - 3594 Cactus (强连通)

http://acm.hdu.edu.cn/showproblem.php?pid=3594 判断给定的图是否是强连通的,并且每条边都只属于一个连通分量. 判断强连通只需要判断缩点之后顶点数是否为1即可, 然后在缩点的过程中,如果已经产生环,并且当前结点的父节点还有父节点,则必定有多个环, 最后还要判断每个结点都要只属于一个联通分量,否则不符合要求. 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath>

[bzoj1023][SHOI2008]cactus 仙人掌图 (动态规划)

Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6,5,4). (7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两个的简单回路里.另外,第三张图也 不是仙人图,因为它并不是连通图.显

bzoj1023: [SHOI2008]cactus仙人掌图

学习了一下圆方树. 圆方树是一种可以处理仙人掌的数据结构,具体见这里:http://immortalco.blog.uoj.ac/blog/1955 简单来讲它是这么做的:用tarjan找环,然后对每个环建立一个新点,然后将环上的边删去,并环上的每个点都连到新点上.这样我们就可以把一个环缩成一个菊花图,重复这么做,一棵仙人掌就变成一棵树啦!这棵树就叫做圆方树,其中原点叫圆点,新点叫方点. 圆方树和原仙人掌很相似,而且它又是一棵树,于是我们就可以在上面dp啦!不过要注意的是对于方点的处理,不能直接