P3387 【模板】缩点 (tarjan SCC缩点)
【模板】缩点
题目描述
给定一个 个点 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式
第一行两个正整数
第二行 个整数,其中第 个数 表示点 的点权。
第三至 行,每行两个整数 ,表示一条 的有向边。
输出格式
共一行,最大的点权之和。
样例 #1
样例输入 #1
2 2 1 1 1 2 2 1
样例输出 #1
2
提示
对于 的数据,,,。
代码
// Problem: P3387 【模板】缩点 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P3387 // Memory Limit: 125 MB // Time Limit: 1000 ms // Created Time: 2022-07-20 14:56:17 // // Powered by CP Editor (https://cpeditor.org) //fw #include<iostream> #include<cstdio> #include<fstream> #include<algorithm> #include<cmath> #include<deque> #include<vector> #include<queue> #include<string> #include<cstring> #include<map> #include<stack> #include<set> #include<climits> #define zp ios::sync_with_stdio(false);cin.tie(0); cout.tie(0); #define pii pair <int, int> #define endl '\n' #define pb push_back #define lc u<<1 #define rc u<<1|1 using namespace std; typedef long long ll; const int INF=0x3f3f3f3f; const int N=4e5+10,M=1e6+10; int n,m,idx,h[N],hs[N],e[N],ne[N]; ll w[N],sum[N],f[N]; int low[N],dfn[N],stk[N],times,top,siz[N],scc_cnt,id[N]; bool in_stk[N]; void tarjan(int u) { dfn[u]=low[u]=++times;//初始化时间戳和能访问到的最小时间戳 //不能写成times++,因为是以dfn==0为条件判断是否被计算过的 stk[++top]=u,in_stk[u]=true;//把当前点加入联通分量的栈 //写++ttop是为了后面取出栈顶元素是对应,这里也可以写ttop++,同时将后面改成t=stk[--ttop] for(int i=h[u];~i;i=ne[i])//遍历当前点能访问到的所有邻点 { int j=e[i]; if(!dfn[j])//如果没有计算过 { tarjan(j);//递归计算 low[u]=min(low[u],low[j]);//用邻点能访问到的最小时间戳更新当前点 } else if (in_stk[j])low[u]=min(low[u],dfn[j]);//如果已经在栈中就用时间戳更新 } if(low[u]==dfn[u])//如果这个点是联通分量中的最高点 { ++scc_cnt;//计数 int y; //记录该联通分量中所有点的信息 do { y=stk[top--]; //将连通分量里的点出栈 in_stk[y]=false; id[y]=scc_cnt;//记录点在哪个连通分量 siz[scc_cnt]++;//记录连通分量中点数 sum[scc_cnt]+=w[y];//记录联通分量中点权总和 } while(u!=y); } } void add(int h[],int a,int b) { e[idx]=b; ne[idx]=h[a]; h[a]=idx++; } int main() { cin>>n>>m; for(int i=1;i<=n;i++)cin>>w[i];//输入点权 //初始化原图和缩点后图的表头 memset(h,-1,sizeof h); memset(hs,-1,sizeof hs); //前向星建原图 while(m--) { int a,b; cin>>a>>b; add(h,a,b); } //求scc for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); //缩点后建立新图 for(int i=1;i<=n;i++) for(int j=h[i];~j;j=ne[j]) { int k=e[j]; int a=id[i],b=id[k]; if(a!=b) { add(hs,a,b); } } //统计路径总和最大值 for(int i=scc_cnt;i;i--)//缩点后scc从大到小即为拓扑序,直接在DAG上递推统计答案 { //如果这个点没有被计算过,则说明是起点,初始化 if(!f[i]) { f[i]=sum[i]; } //遍历邻点并递推状态 for(int j=hs[i];~j;j=ne[j]) { int k=e[j]; if(f[k]<f[i]+sum[k]) { f[k]=f[i]+sum[k]; } } } //计算答案 ll ans=0; for(int i=1;i<=scc_cnt;i++) ans=max(ans,f[i]); cout<<ans<<endl; return 0; }
一个菜鸡
本文作者:Avarice_Zhao
本文链接:https://www.cnblogs.com/avarice/p/16500338.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步