传送门:
P3387 【模板】缩点 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P3387
问题分析
首先为了节省空间和提高效率,使用链式前向星来存储图,图论基操了属于。由于是有向图,所以是强连通图的题型。tarjin算法走一遍后得到一个新的图,这个图不存在强连通分量,形成一个有向无环图,显然可以用拓扑排序。
代码:
import java.io.*;
import java.util.*;
public class Main {
static int maxn=10001,maxm=100001; //n和m的最大值
static int n,m,cnt,num,idx,top; //后面用到的所有全局静态变量
static int[] eu=new int[maxm],ev=new int[maxm];
static int[] vv=new int[maxm],to=new int[maxm];//vv,to,he是链式前向星标配
static int[] he=new int[maxn],stk=new int[maxn];//stk模拟栈
static int[] dfn=new int[maxn],low=new int[maxn];//dfn,low,scc,stk是tarjin标配
static int[] scc=new int[maxn],ind=new int[maxn];//ind记录入度
static int[] ga=new int[maxn],dis=new int[maxn];//ga记录每一个点的点权(tarjin后新的图)
static int[] arr=new int[maxn]; //arr记录每一个点的点权(输入图)
public static void main(String[] args) throws IOException {
//输入部分
n=nextInt();m=nextInt();
for(int i=1;i<=n;i++) arr[i]=nextInt();
for(int i=1;i<=m;i++){
eu[i]=nextInt();
ev[i]=nextInt();
addEdge(eu[i],ev[i]);
}
for(int i=1;i<=n;i++)
tarjin(i);
Arrays.fill(vv,0);
Arrays.fill(to,0);
Arrays.fill(he,0);cnt=0;
for(int i=1;i<=m;i++){
if(scc[eu[i]]!=scc[ev[i]]){
ind[scc[ev[i]]]++;
addEdge(scc[eu[i]],scc[ev[i]]);
}
}
int ans = topo();
out.println(ans);
out.close(); //close不能忘
}
//标准topo排序
static int topo(){
int ans=0;
Queue<Integer> q = new LinkedList<>();
for(int i=1;i<=idx;i++){
if(ind[i]==0){
dis[i]=ga[i];
q.offer(i);
}
}
while(!q.isEmpty()){
Integer u = q.poll();
for(int i=he[u];i>0;i=to[i]){
int v=vv[i];
ind[v]--;
if(ind[v]==0) q.offer(v);
dis[v]=Math.max(dis[v],dis[u]+ga[v]);
ans=Math.max(ans,dis[v]);
}
}
for(int i=1;i<=idx;i++)
ans=Math.max(ans,dis[i]);
return ans;
}
static void tarjin(int root){
if(dfn[root]>0) return; //若该点访问过了,return
dfn[root]=low[root]=++num; //更新dfn和low
stk[++top]=root; //更新栈
for(int i=he[root];i>0;i=to[i]){
int v=vv[i];
if(dfn[v]==0){ //如果该点没有访问过
tarjin(v); //那么递归下去
low[root]=Math.min(low[v],low[root]);
} else if(scc[v]==0) {
low[root]=Math.min(dfn[v],low[root]);
}
}
//若dfn和low一致,说明栈中root上面所有点共同组成一个强连通子图
if(dfn[root]==low[root]){
idx++;
for(;;){
//将这些点合并,在新图中成为一个点
int x=stk[top--]; //出栈
scc[x]=idx; //scc对应新图数据
ga[idx]+=arr[x]; //更新新图点的点权
if(x==root) break;
}
}
}
public static void addEdge(int u,int v){
vv[++cnt]=v;
to[cnt]=he[u];
he[u]=cnt;
}
//以下为java魔法快速输入
static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
public static int nextInt() throws IOException{
in.nextToken();
return (int)in.nval;
}
//在这里并没有用到,多写无碍
public static String nextString() throws IOException {
in.nextToken();
return in.sval;
}
}