模板 - 图论 - 基环树

基环树也可以直接套强连通缩点给秒了,但是事实上假如不需要缩点的话有更简单的写法。

下面是一种示例,必须是内向基环树,注意内向基环树的dfs上面有好几个时点:

0、进入环的时候,有时是从入度为0的点进入可能会有特殊操作,但是一般来说进入的时候主要是各个操作的初始化值。
1、当 color[u]!=0&&color[u]==c 时,第一次找到环的入口,可以这时候处理入口的值,但一般只交给3去做就好了。
2、当 color[u]!=0&&color[u]!=c 时,意味着找到其他dfs找过的点,有可能是环的其中一个入口,也有可能是树分叉,因为不是从入度为0的点开始找的甚至可能本身是一条链,这个时候处理不在环中的操作(op1)。
3、当 incirle 时,意味着在环中,处理在环中的操作(op2)。当 u==incirle 时,从环的入口退出,把在环中的标记清空,并处理退出环的操作(op3)。
4、以上都不是,意味着不在环中,处理不在环中的操作(op1)。

总之大概是这个样子,基环树也并不是都要存入度的,很多情况是可以直接用op1来继承,而且op1是躲不开的,因为有时候树会分叉。

const int MAXN = 2e5;

int n, G[MAXN + 5];
int color[MAXN + 5], cntcolor;

int incircle;
void dfs(int u, int c) {
    if(color[u]){
        if(color[u] == c){
            incircle = u;
            return;
        }
        //op1
        return;
    }
    color[u] = c;
    dfs(G[u], c);
    if(incircle) {
        //op2
        if(u == incircle) {
            //op3
            incircle = 0;
        }
    }
    //op1
}

void test_case() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &G[i]);
    cntcolor = 0;
    for(int i = 1; i <= n; ++i) {
        if(!color[i]) {
            ++cntcolor;
            //op0
            dfs(i, cntcolor);
        }
    }
}

但是还是希望使用缩点法,缩点法没有这么多时点,全部都是建新图。

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/11980305.html

时间: 2024-10-12 23:19:14

模板 - 图论 - 基环树的相关文章

bzoj1040(ZJOI2008)骑士——基环树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1040 基环树的模板. 套路就是把环断开,先把一端作为根节点,强制不选:再把另一端作为根节点,强制不选. 人家的这个判断环的方法真好!还顺便没有连上环的那条边,省下了在函数里判断. 别忘了有多棵基环树! #include<iostream> #include<cstdio> #include<cstring> #define ll long long using n

基环树

树: 基环树: 就是比平常的树多了一条边..构成了一个环 做法就是 dfs去找环 然后删掉环上的任意一条边,记下u和v 分别以u 和 v 为祖结点 进行树形dp 分别求出不要u 和  不要v的值 取大的 即可 模板题: 骑士 HYSBZ - 1040 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是

『寝室管理 基环树点分』

寝室管理 Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了. 宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互达.最近,T128被要求对一条路径上的所有寝室进行管理,这条路径不会重复经过某个点或某条边.但他不记得是哪条路径了.他只记得这条路径上有不少于k个寝室.于是,他想请T64帮忙数一下,有多少条这样

[noi2013]快餐店 基环树dp,单调队列维护最大值和次大值

#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 220000 #define inf 0x3ffffffffffffffLL typedef long long ll; int v[N],e[N],ne[N],nn,w[N]; void add(int x,int y,int z){ ne[++nn

【原创】洛谷 LUOGU P3373 【模板】线段树2

P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含三个整数N.M.P,分别表示该数列数字的个数.操作的总个数和模数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k 操作2: 格式:2 x

【BZOJ1589】【USACO 2008 Dec Gold】 1.Trick or Treat on the Farm 基环树裸DP、

没测样例一遍A这真是-- 题意:每个点都有且仅有一个出边(可以出现自环),然后这样一个点出发就会走过且一定走过某些点. 问每个点出发都会走过几个点. 首先这是基环树无疑. 然后就是裸DP了. 这个的关键就是找环,仅此. #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 101000 using namespace std; int n

[HDU 5304] 基环树计数

题意 给定一张 n 个点 m 条边的无向图, 问其有多少个生成基环树. n <= 16 , 无重边, 无自环. 分析 状压DP 处理每个状态对应的环的个数. 枚举所有状态, 缩点后重标号, 使用 Matrix 定理计算答案. 实现 枚举环: f[s][i] 表示从 s 状态中的最小值出发, 终点在 i 的方案数. 边界 f[2 ^ i][i] = 1 . 统计答案的时候, 对于 f[s][i] , 若 s 中的个数大于 2 , 且 i 能到达 s 状态中的最小值, 则 cnt[s] += f[s

【bzoj1040】[ZJOI2008]骑士 并查集+基环树dp

题目描述 Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队.于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶.骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾.每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶

洛谷P3373 【模板】线段树 2

 P3373 [模板]线段树 2 47通过 186提交 题目提供者HansBug 标签 难度提高+/省选- 提交  讨论  题解 最新讨论 为啥WA(TAT) 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含三个整数N.M.P,分别表示该数列数字的个数.操作的总个数和模数. 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值. 接下来M行每行包含3或4