P3387 【模板】缩点 题解 (Tarjan)

题目链接

P3387 【模板】缩点

解题思路

这几天搞图论,好有趣hhh,多写几篇博客。

上次学\(Tarjan\)求割点,这次缩点。

思路大概是多一个栈和染色的步骤,每次\(Tarjan\)的时候把点入栈,如果某个点(比较像割点但不完全是)的\(DFS\)子树都搜不到它祖宗,那么接下来进行的遍历操作必然与该点不能形成强连通分量,所以可以遇到\(low[p]==dfn[p]\)的点就把栈里面的东西全弹出来,染色表示这是一个强连通分量。

对于这个题,强连通分量之间再进行连边,记忆化搜索(类似树形DP)即可。

AC代码

#include<stdio.h>
#include<string.h>
#define N 100010
#define M 10010
#define min(a,b) (a>b?b:a)
#define max(a,b) (a<b?b:a)
int n,m;
int v[M],x[N],y[N];

struct Edge{
    int end,near;
}e[N];
int head[M],cnt;
void add(int a,int b){
    e[++cnt].end=b;
    e[cnt].near=head[a];
    head[a]=cnt;
}

int dfn[M],tot,sta[M],top,vis[M],low[M];
int color[M],num,val[M];
void tarjan(int p){
    dfn[p]=low[p]=++tot;
    sta[++top]=p;vis[p]=1;
    int i;
    for(i=head[p];i;i=e[i].near){
        int q=e[i].end;
        if(!dfn[q])tarjan(q),low[p]=min(low[p],low[q]);
        else if(vis[q])low[p]=min(low[p],low[q]);
    }
    if(dfn[p]==low[p]){
        num++;
        while(sta[top+1]!=p){
            color[sta[top]]=num;
            vis[sta[top]]=0;
            val[num]+=v[sta[top]];
            top--;
        }
    }
}

int f[M];
void dp(int p){
    int i;
    for(i=head[p];i;i=e[i].near){
        int q=e[i].end;
        if(!f[q])dp(q);
        f[p]=max(f[p],f[q]);
    }
    f[p]+=val[p];
}

int main(){
    int i,ans=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&v[i]);
    for(i=0;i<m;i++)scanf("%d%d",&x[i],&y[i]),add(x[i],y[i]);
    for(i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);//tarjan缩点
    memset(head,0,sizeof(head));cnt=0;//清空边
    for(i=0;i<m;i++)//强连通分量之间连边
        if(color[x[i]]^color[y[i]])add(color[x[i]],color[y[i]]);
    for(i=1;i<=num;i++)if(!f[i])dp(i),ans=max(ans,f[i]);//DP
    printf("%d",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/Potassium/p/10444067.html

时间: 2024-11-10 13:00:40

P3387 【模板】缩点 题解 (Tarjan)的相关文章

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA

[BZOJ 1179]ATM题解 Tarjan缩点+SPFA Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,

HDU ACM 1827 Summer Holiday-&gt;强连通分量+缩点(tarjan算法)

分析:首先求强连通分量的个数,然后进行缩点,最后求出最终答案. 1.求强连通分量的个数使用tarjan算法. 2.缩点为另外一个图,通过tarjan算法求出的结果进行.缩点后的图中求出每个点的入度. 3.求出每个强连通分量中的最小花费. 4.根据缩点后图的入度求出最终结果. #include<iostream> #include<vector> #include<stack> using namespace std; vector<int> map[1002

Luogu P3811 [模板]乘法逆元 题解报告

题目传送门 [题目大意] 给定$n$,求$1-n$在膜$p$意义下的乘法逆元. [思路分析] 好的原本我只会求单个数的逆元,然后被告知了这道题之后发现自己不会做(我果然还是太弱了),于是就学了一下递推求逆元. 设$p=k*i+r$,则可得$k*i+r\equiv0(mod\ p)$,然后乘上$i^{-1},r^{-1}$即可得到$k*r^{-1}+i^{-1}\equiv0(mod\ p)$ 由于$k=\lfloor \frac{p}{i}\rfloor,r=p\ mod\ i$,所以$i^{-

洛谷P3387 【模板】缩点

洛谷P3387 [模板]缩点 题目背景 缩点+DP 题目描述 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 输入输出格式 输入格式: 第一行,n,m 第二行,n个整数,依次代表点权 第三至m+2行,每行两个整数u,v,表示u->v有一条有向边 输出格式: 共一行,最大的点权之和. 输入输出样例 输入样例#1: 2 2 1 1 1 2 2 1 输出样例#1: 2 说

【模板】缩点 tarjan+dp

题目背景 缩点+DP 题目描述 给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次. 输入输出格式 输入格式: 第一行,n,m 第二行,n个整数,依次代表点权 第三至m+2行,每行两个整数u,v,表示u->v有一条有向边 输出格式: 共一行,最大的点权之和. 输入输出样例 输入样例#1: 复制 2 2 1 1 1 2 2 1 输出样例#1: 复制 2 说明 n<=10^

POJ 1523 SPF 割点与桥的判断算法-Tarjan

题目链接: POJ1523 题意: 问一个连通的网络中有多少个关节点,这些关节点分别能把网络分成几部分 题解: Tarjan 算法模板题 顺序遍历整个图,可以得到一棵生成树: 树边:可理解为在DFS过程中访问未访问节点时所经过的边,也称为父子边 回边:可理解为在DFS过程中遇到已访问节点时所经过的边,也称为返祖边.后向边 对根节点u,若其有两棵或两棵以上的子树,则该根结点u为割点: 对非叶子节点u(非根节点),若其子树的节点均没有指向u的祖先节点的回边,说明删除u之后,根结点与u的子树的节点不再

【基本算置顶】各大算法&amp;&amp;数据结构模板

板子,全是板子 更新日志(从2018.11.19开始) 2018.12.02 : 更新了数据结构->扫描线 2018.11.22 : 更新了数据结构->平衡树->FHQ Treap->维护区间操作 2018.11.20 : 更新了数论->博弈论->nim游戏 2018.11.20 : 更新了数据结构->平衡树->FHQ Treap 观摩本蒟蒻板子库的大佬数: 不断更新 一.数论 1.快速幂 2.欧拉函数 3.乘法逆元(线性求逆) 4.线性筛素数 5.扩展欧几

Tarjan算法专练

1.迷宫城堡 题意:给一个图判断是否是强连通图. 题解:利用Tarjan计算图中强连通分量的个数,如果为1则是强连通图,否则不是. #include<bits/stdc++.h> using namespace std; const int N = 2e4+100; typedef long long ll; vector<int> G[N]; bool is_instack[N]; int dfn[N],low[N]; stack<int> sta; int n,m,

Tarjan算法求强连通分量

一.操作过程:tarjan算法的基础是DFS.我们准备两个数组Low和Dfn.Low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的 Dfn值(很绕嘴,往下看你就会明白),Dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的.根据以下几条规则,经过搜索遍历该图(无需回溯)和 对栈的操作,我们就可以得到该有向图的强连通分量. 1.数组的初始化:当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间. 2.堆栈:每搜索到一个点,将它压入栈顶. 3.当点p有与点p’相连时,如