Tarjan小结-缩点
和lca一样一直忘写了...
仔细看题可以得知,如果将传递关系建有向图,那么要输出图中最小环的大小。我的理解中最裸的tarjan用于判环
int tim,top,tot;
int dfn[MAXN],low[MAXN],flag[MAXN],stac[MAXN],scc[MAXN],cnt[MAXN];
void tarjan(int u){
dfn[u]=low[u]=++tim;
stac[++top]=u,flag[u]=1;
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(flag[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
scc[u]=++tot;
flag[u]=0;
cnt[tot]++;
while(stac[top]!=u){
cnt[tot]++;
scc[stac[top]]=tot;
flag[stac[top]]=0;
top--;
}
--top;
}
}
...
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
本题中,tarjan判scc所要用到的变量是时间tim,栈顶位置top,scc编号tot。
以及前文提到的dfn[],low[],flag[],和模拟的栈stac[],以及存储点i所属scc的scc[i],第i个scc的大小cnt[i]
普遍地,scc[i]常用于将缩点后的图以每个scc作为点构造新图 例题
注意到,当前打算劫掠的atm与这个atm所属的scc都是可以劫掠的,所以完全可以把每个scc的钱数集合起来作为新图里一个点的钱数,跑一边单源多汇即可
#include<bits/stdc++.h>
#define MAXN 500001
using namespace std;
int n,m,p,s,v[MAXN],e[MAXN],val[MAXN],ans;
vector<int>edge[MAXN];
vector<int>nedge[MAXN];
int tim,tot,top;
int dfn[MAXN],low[MAXN],flag[MAXN],stac[MAXN],scc[MAXN];
int ind[MAXN],tps[MAXN],mxd[MAXN];
void tarjan(int u){
dfn[u]=low[u]=++tim;
flag[u]=1;stac[++top]=u;
for(int i=0;i<edge[u].size();i++){
int v=edge[u][i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(flag[v])low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u]){
scc[u]=++tot;
flag[u]=0;
val[tot]=v[u];
while(stac[top]!=u){
scc[stac[top]]=tot;
val[tot]+=v[stac[top]];//在处理scc的时候顺带将新点i的钱数val[i]处理出来
flag[stac[top]]=0;
--top;
}
top--;
}
}
void dfs(int u,int w){//远离spfa
for(int i=0;i<nedge[u].size();i++){
int v=nedge[u][i];
if(w+val[v]>mxd[v]){
mxd[v]=w+val[v];
dfs(v,w+val[v]);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
edge[u].push_back(v);
}
for(int i=1;i<=n;i++)scanf("%d",&v[i]);
scanf("%d%d",&s,&p);
for(int i=1;i<=p;i++)scanf("%d",&e[i]);
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);//板子
for(int u=1;u<=n;u++){
for(int i=0;i<edge[u].size();i++){//新点的连边操作
int v=edge[u][i];
if(scc[u]!=scc[v]){//如果两个旧点所属的scc不同,说明他俩在新图中也需要连边,直接将他们两个所属的scc头目链接即可
nedge[scc[u]].push_back(scc[v]);
ind[scc[v]]++;
}
}
}
//于是得到了一个由scc头目组成的,点权代表着scc钱数和的新图被建好了
mxd[scc[s]]=val[scc[s]];//自己研究的mxd写法,mxd[i]表示走到i时的最优答案,广泛地应用于答案贪心策略成立的题目中
//本题中,如果当前路径走到i的钱数并不是走到i的最优钱数,那么当前路径之后的一切经过的点的钱数都是有更优情况的,即从i最优钱数路径出发可以被全部刷新。
dfs(scc[s],val[scc[s]]);
for(int i=1;i<=p;i++)ans=max(ans,mxd[scc[e[i]]]);
printf("%d",ans);
return 0;
}
还有在新图里跑dp的情况,不过那是dp的事,在此不提。