白书训练指南 5.2 深度优先遍历 && kuangbin专题九连通图 (qwq,现在才开,太懒了,我不愧是懒狗)

一些概念

点双模板(包含 割顶,桥,无向图

#include <cstring>
#include <algorithm>
#include <stack>
#include <vector>
#define mst(x,a) memset(x,a,sizeof(x))
#define fzhead EDGE(int _u, int _v, int _next)
#define fzbody u(_u), v(_v), next(_next)
#define pb push_back
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) for(int i=(x); i<(y); i++)
using namespace std;
const int maxm=2e6+10;
const int maxn=1e3+10;
struct EDGE{
    int u,v,next;
    EDGE(){}
    fzhead:fzbody{}
}e[maxm];
int dfn[maxn],low[maxn],cut[maxn],bccno[maxn],dfs_clock,bcc_cnt;
/// bccno[maxn]存一个点是哪一个 点双连通分量。
int head[maxn],tot;
vector<int> bcc[maxn], bridge;
stack<EDGE> s;
void add(int bg, int to){
    e[tot]=EDGE(bg,to,head[bg]);
    head[bg]=tot++;
}
int root;
void tarjan(int u, int f){
    dfn[u]=low[u]=++dfs_clock;
    int child=0;
    for(int i = head[u]; i != -1; i = e[i].next){
        int v = e[i].v;
        EDGE edge = {u,v,0};
        if(!dfn[v]){
            s.push(edge);
            child++;
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(low[v]>dfn[u])bridge.push_back(i);
            if(low[v] >= dfn[u]){
                if(u != root||child > 1)cut[u]=1;///判断是否是割点
                bcc[++bcc_cnt].clear();
                for(;;){
                    EDGE x = s.top();s.pop();
                    if(bccno[x.u] != bcc_cnt){
                        bcc[bcc_cnt].pb(x.u); bccno[x.u] = bcc_cnt;
                    }
                    if(bccno[x.v] != bcc_cnt){
                        bcc[bcc_cnt].pb(x.v); bccno[x.v] = bcc_cnt;
                    }
                    if(x.u == u && x.v == v)break;
                }
            }
        }else if(v != f && dfn[v] < dfn[u]){///有反向边
            s.push(edge);
            low[u] = min(low[u],dfn[v]);
        }
    }
}
void init(){
    tot=2;
    bcc_cnt=dfs_clock=0;
    mst(head,-1);
    mst(dfn,0);
    mst(low,0);
    mst(cut,0);
    mst(bccno,0);
    mst(odd,0);
    while(!s.empty())s.pop();
}

强联通模板(包含 割顶,桥,双联通,有向图

#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#define mst(x,a) memset(x,a,sizeof(x))
#define fzhead EDGE(int _to, int _next)
#define fzbody to(_to), next(_next)
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) fori(int i=(x); i<(y); i++)
using namespace std;
const int maxn=5000+10;
const int maxm=2e4+10;
struct EDGE{
    int to, next;
    EDGE(){}
    fzhead:fzbody{}
}e[maxm];
int head[maxn], cut[maxn], vis[maxn],bcc[maxn],bcc_num[maxn],col[maxn];
int fa[maxn],pre[maxn], low[maxn], dfn[maxn],bcc_cnt,tot, dfs_clock;
int bridge[maxm],in[maxn];
void add(int bg, int to){
    e[tot]=EDGE(to,head[bg]);
    head[bg]=tot++;
}
stack<int>s;
void init(){
    tot=2;
    dfs_clock=bcc_cnt=0;
    mst(head,-1);
    mst(vis,0);
    mst(bcc,0);
    mst(fa,0);
    mst(pre,0);
    mst(low,0);
    mst(dfn,0);
    mst(bcc_num,0);
    mst(col,0);
    mst(cut,0);
    mst(in,0);
    while(!s.empty())s.pop();
}
int root;
void tarjan(int u, int f){
    dfn[u]=low[u]=++dfs_clock;
    s.push(u);
    fa[u]=f;
    vis[u]=1;
    int child=0;
    for(int i=head[u]; i!=-1; i=e[i].next){
        int v=e[i].to;
        if(!dfn[v]){
            pre[v]=i^1;
            child++;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&(u!=root||child>1))cut[u]=1;
        }else if(v!=f&&dfn[v]<dfn[u])low[u]=min(low[u],dfn[v]);
        ///else if(vis[v])low[u]=min(low[u],dfn[v]);
        //有向图,时才用这句话。
    }
    if(low[u]==dfn[u]){
        col[u]=++bcc_cnt;
        vis[u]=0;
        int v;
        bridge[pre[u]^1]=bridge[pre[u]]=1;
        do{
            v = s.top();s.pop();
            col[v]=bcc_cnt;
            bcc_num[bcc_cnt]++;
        }while(v!=u);
    }
}
int n,m;
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n>>m;
    int u,v;
    init();
    For(i,1,m){
        cin>>u>>v;
        add(u,v);add(v,u);
    }
    For(u,1,n)if(!dfn[u])dfs_clock=0, root=u, tarjan(u,-1);
    //cout<<bcc_cnt<<endl;
    For(u,2,n){
        int a=col[u],b=col[fa[u]];
        if(a==b)continue;
        in[a]++,in[b]++;
    }
    int ans=0;
    For(i,1,bcc_cnt)if(in[i]==1)ans++;
    //cout<<ans<<endl;
    cout<<(ans+1)/2<<endl;
    return 0;
}

Divide Groups HDU - 4751

本题就是裸的二分图染色。(本题的题意。假如互相认识,那么就随便染)

Wrestling Match HDU - 5971

  1. 由于题目已经有事先分配的两个集合。所以先进行两次dfs。
  2. 最后再进行一次常规dfs即可

1.模板题(tarjan)

Network of Schools POJ - 1236

模板题(对于ans2,可以转换为DAG中加最少条边,使得DAG变为强联通图

信息传递 LibreOJ - 2421

在求tarjan时,把每个环的长度取一个min

Road Construction POJ - 3352

low用来缩点。

2.割点

Network UVA - 315

求割点的模板

3. 桥

Critical Links UVA - 796

4. 双联通分量

Redundant Paths POJ - 3177

求完双联通后,要思考一下。

5. 点-双连通分量(无向图)

Knights of the Round Table UVA - 1364

一道挺有意思的,里面有奇圈的证明。(也是这一道让我知道了无向图的tarjan和有向图tarjan的区别

Mining Your Own Business UVA - 1108

求 点双连通 涉及 一点组合知识

6. 强连通分量 (有向图)

  1. 一般求一次强连通分量,之后缩点

Proving Equivalences UVA - 12167

本题就是求完后,进行一次缩点即可。之后思维了

The Largest Clique UVA - 11324

先求强连通分量,之后缩点,在DAG上进行简单dp即可(求一个最长路)。

Calling Circles UVA - 247

本题是求tarjan的强连通分量的模板题。之后输出每个强连通分量(不要求顺序)

7. 2-sat问题

主要就是问题的转换,之后写出 或运算(a ∨ \lor b),或运算即为限制条件。
关键
找到限制条件,根据 子句(条件来定义 边的个数

D F S DFS DFS染色法的模板

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <cmath>
#define pb push_back
#define fzhead EDGE(int _to, int _next)
#define fzbody to(_to), next(_next)
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) for(int i=(x); i<(y); i++)
using namespace std;
const int maxn=2000+10;
const int maxm=2000*2000+10;
struct EDGE{
    int to,next;
    EDGE(){}
    fzhead:fzbody{}
}e[2*maxm*2];
int head[2*maxn],tot;
bool mark[maxn*2];
///int s
stack<int>s;
void add(int bg, int to){
    e[tot]=EDGE(to,head[bg]);
    head[bg]=tot++;
}
void add_clause(int x, int xval, int y, int yval){
    x=x*2+xval;
    y=y*2+yval;
    add(x^1,y);
    add(y^1,x);
}
bool dfs(int x){
    if(mark[x^1])return false;
    if(mark[x])return true;
    mark[x]=true;
    s.push(x);
    for(int i=head[x]; i!=-1; i=e[i].next){
        int v=e[i].to;
        if(!dfs(v))return false;
    }
    return true;
}
int n;
bool twoset(){
    for(int i=0; i<2*n; i+=2){//fori(i,0,2*n){
        if(!mark[i] && !mark[i+1]){
            while(!s.empty())s.pop();
            if(!dfs(i)){
                while(!s.empty())mark[s.top()] = false,s.pop();
                if(!dfs(i+1))return false;
            }
        }
    }
    return true;
}
void init(){
    mst(mark,0);tot=2;
    mst(head,-1);
    while(!s.empty())s.pop();
}

Now or later UVA - 1146

二分 + 2-sat。

Astronauts UVA - 1391

2 - sat

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值